Using ifstream in SdFat with csv files

SdFat's ifstream provides a way to read and write comma separated values. But does it only work with number types of data? When I modified the sample readCSV sketch to include char lg[2] for a one character string, the sketch crashed. Here it is:

I need to read and write csv files that include char arrays and floating numbers. Can ifstream do that? Thanks.

readCSV.ino (2.18 KB)

ifstream is for use by experienced C++ programmers with knowledge of the C++ iostreams library.

I suggest you use another method to read your file since you have no experience with ifstream http://www.cplusplus.com/reference/fstream/ifstream/.

Your program crashed since this statement:

  char lg[2];
  sdin >> lg;

Skips white space, reads characters into lg until white space is found then terminates with a zero byte. Since you are reading a csv file this will read many characters and corrupt memory.

This function could read your string but I don't recommend you try it.

 istream& get (streambuf& sb, char delim);

I don't understand why do you want a CSV file just store the raw values and do the conversion on the computer. Don't bother converting to plain text on the arduino. Also why do you need ifstream?

I didn't mean you shouldn't use CSV files. CSV is a good choice for data exchange if performance isn't a problem.

Also ifstream is a good choice for parsing text files. It's just that the iostreams library is complex for a beginner.

The program displays and should log packet radio position data. CSV is desired for use by other programs. I picked the readCSV sketch from SdFat because of its name, but now I see it would not be good for that since the data is both numbers and char arrays.

The function will have variables passed to it, and should build them into a string with fields separated by commas. That part is easy. It should save that to a card file, along with an index or line number. A retrieval function will be added, and this should be able to search the file and read the correct line. I know how to split the comma separated string into the variables.

What function(s) in the SdFat library would be best for such storage and retrieval, and are there examples like this available?

Thanks for any help. I'm not exactly a beginner, but it has been over 20 years, and my last language was Turbo Pascal!

ifstream is good for all data types. It is the standard C++ way to read text files.

You may have trouble using C++ streams since the C++ standard streams library is complex.

Here is a version of readCSV that includes a string field, a long, and two floats:

/*
 *  This example reads a simple CSV, comma-separated values, file.
 *  Each line of the file has three values, a long and two floats.
 */
#include <SdFat.h>

// SD chip select pin
const uint8_t chipSelect = SS;

// file system object
SdFat sd;

// create Serial stream
ArduinoOutStream cout(Serial);

char fileName[] = "3V_FILE.CSV";
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt_P(PSTR(s))
//------------------------------------------------------------------------------
// read and print CSV test file
void readFile() {
  char string[10];
  long lg;
  float f1, f2;
  char c1, c2, c3;
  
  // open input file
  ifstream sdin(fileName);
  
  // check for open error
  if (!sdin.is_open()) error("open");
  
  // read until input fails
  while (sdin.get(string, sizeof(string),',') >> c1 >> lg >> c2 >> f1 >> c3 >> f2) {
    // skip '\n' at end of line
    sdin >> ws;
    // error in line if not commas
    if (c1 != ',' || c2 != ',' || c3 != ',') error("comma");
    
    // print in six character wide columns
    cout << string << ':' << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl;
  }
  // Error in an input line if file is not at EOF.
  if (!sdin.eof()) error("readFile");
}
//------------------------------------------------------------------------------
// write test file
void writeFile() {

  // create or open and truncate output file
  ofstream sdout(fileName);
  
  // write file from string stored in flash
  sdout << pstr(
    "line 1,1,2.3,4.5\n"
    "line 2,6,7.8,9.0\n"
    "line 3,9,8.7,6.5\n"
    "line 4,-4,-3.2,-1\n") << flush;

  // check for any errors
  if (!sdout) error("writeFile");
  
  sdout.close();
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while (!Serial) {} // wait for Leonardo
  cout << pstr("Type any character to start\n");
  while (Serial.read() <= 0) {}
  delay(400);  // catch Due reset problem
  
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
  
  // create test file
  writeFile();
  
  cout << endl;

  // read and print test
  readFile();  
  
  cout << "\nDone!" << endl;
}
void loop() {}

Its output is

Type any character to start

line 1: 1 2.30 4.50
line 2: 6 7.80 9.00
line 3: 9 8.70 6.50
line 4: -4 -3.20 -1.00

Done!

You can read lines using the get() or getline() function with ifstream. get() removes the terminating delimiter. See the SdFat getline example.

You could also use the SdFile class and use the fgets() function to read lines. See the SdFat fgets example.

fat16lib:
You can read lines using the get() or getline() function with ifstream. get() removes the terminating delimiter. See the SdFat getline example.

You could also use the SdFile class and use the fgets() function to read lines. See the SdFat fgets example.

OK, I'll look at those. Thanks again.

Solar energy file.

I am using ofstream/ifstream to write csv file that get sent to browser where I plot it with highchart.
I update the file every day and if the month has changed I add a new record. What I have works, but I don't like my method much as I read the file , build a temporary file either updating the last record if still in the current month or generate a new record if the month has changed. Then copy the temporary file back . As the file gets large this could be slow.
It seems to me that it would be better to just save the last records starting position and Read/Update it if month hasn't changed or write a new record (append to file) if the month has changed.
The question is I don't know how to use file positioning when using ofstream/ifstream . Any pointers?
The csv file:
timestamp,whmin,spmin,sbtus,whbtus,mth,day,yr <----Header record for highchart plot
1393286270,0.00,0.00,0.00,0.00,1,31,2014 <-- last day of month
1393286270,122.50,55.30,0.00,0.00,2,24,2014 <--- todays data
unix time stamp is not correct here, but you get the idea.

Note that as the data gets stored the length of each record may not be the same depending on the data.