myFile.read() returns one character. That character can be represented as a number (such as 65 for 'A') or a character, or whatever you want. Everything is just a number.
Letters are just our personal representation of certain numbers as humans. You could think of it as the numbers represented in base 256, where each value between 0 and 255 is assigned a unique symbol.
Serial.write() just passes that number verbatim to the computer which then maps it in the serial monitor to the corresponding letter in the ASCII table.
Thanks to the object-oriented and polymorphic nature of the Arduino API (see C++ Polymorphism) there is no difference between reading from a file and reading from serial. They all inherit the same
Stream class that gives you the same functions regardless of what you're reading.
So you could, like Serial, read each byte from the file one at a time until you reach the line ending character(s) (
\n for a Unix file, or
\r\n for a Windows file) and append each to the end of a string (either a
String object, which is frowned upon, though less so on more capable MCUs like the ESP8266 and ESP32, or a
Alternatively you could use one of the helper functions of the
Stream class, such as
readStringUntil(), which reads an entire string up to (and discarding) the specified terminating character and returns a
However both those have problems when it comes to building up an array of all the results, especially if you don't know beforehand how many lines there are in the file - since in C++ arrays have a fixed size.
A better solution, if you really do want it all in RAM (see below), might be instead to, at first, forget the idea of lines and just read the entire file into memory. Once it is in memory you can read through the entire block of data counting the number of
\n bytes (and add one if it doesn't end with
\n). That will give you the number of lines in the file.
You can then allocate an array of pointers with the same number of entries as there are lines.
From there you can use
strtok() on the data to split it into chunks on the
\n bytes and assign each chunk to one of the pointers in your array. That array then becomes a kind of index to the data.
But one has to ask... why do you want to store this data in RAM anyway? You have it in flash, and that is quite fast to access (not as fast as internal flash in a traditional MCU, but still many times faster than an SD card for example). If ease of navigating the data is your goal, maybe you should be thinking of better ways of storing your data. Depending on what that data is maybe a fixed size record based file structure using a
struct to manipulate the data would be a better solution - then you can just seek to multiples of the record (or struct) size to almost instantly get to different entries in the file.
If it's textual data then maybe reading through the file and recording the file offsets of the start of each line in an array (note: two passes - one to count the lines for allocating the array and one for recording the offsets into the array) similar to the indexing of the data block described above would be a better solution. That way you're only storing 4 bytes per entry in your array rather than all the data. Seeking is instant (just seek to the offset stored in the array for the line you want) and you're not having to store all the data in RAM.