Read a Line From File and Push Into Vector in C++
As a data scientist, reading and writing data from/to CSV is one of the most common tasks I do on the daily. R, my language of choice, makes this easy with read.csv()
and write.csv()
(although I tend to utilize fread()
and fwrite()
from the data.table package).
Hot Take . C++ is not R.
As far every bit I know, there is no CSV reader/writer built into the C++ STL. That'south non a knock confronting C++; it'south just a lower level language. If we want to read and write CSV files with C++, nosotros'll have to deal with File I/O, data types, and some low level logic on how to read, parse, and write data. For me, this is a necessary pace in society to build and test more fun programs similar auto learning models.
Writing to CSV
We'll get-go by creating a simple CSV file with ane column of integer data. And we'll give it the header Foo.
#include <fstream> int main () { // Create an output filestream object std :: ofstream myFile ( "foo.csv" ); // Send data to the stream myFile << "Foo \n " ; myFile << "1 \north " ; myFile << "two \n " ; myFile << "3 \due north " ; // Close the file myFile . close (); return 0 ; }
Here, ofstream is an "output file stream". Since it's derived from ostream, we can treat information technology just similar cout (which is too derived from ostream). The result of executing this program is that we get a file called foo.csv in the same directory as our executable. Let's wrap this into a write_csv()
function that'southward a little more than dynamic.
#include <string> #include <fstream> #include <vector> void write_csv ( std :: string filename , std :: string colname , std :: vector < int > vals ){ // Make a CSV file with 1 cavalcade of integer values // filename - the name of the file // colname - the name of the one and just column // vals - an integer vector of values // Create an output filestream object std :: ofstream myFile ( filename ); // Send the column name to the stream myFile << colname << " \n " ; // Send data to the stream for ( int i = 0 ; i < vals . size (); ++ i ) { myFile << vals . at ( i ) << " \n " ; } // Shut the file myFile . shut (); } int main () { // Make a vector of length 100 filled with 1s std :: vector < int > vec ( 100 , 1 ); // Write the vector to CSV write_csv ( "ones.csv" , "Col1" , vec ); return 0 ; }
Cool. Now nosotros can utilize write_csv()
to write a vector of integers to a CSV file with ease. Permit's aggrandize on this to support multiple vectors of integers and corresponding column names.
#include <cord> #include <fstream> #include <vector> #include <utility> // std::pair void write_csv ( std :: cord filename , std :: vector < std :: pair < std :: string , std :: vector < int >>> dataset ){ // Make a CSV file with one or more columns of integer values // Each cavalcade of information is represented by the pair <cavalcade proper name, column data> // as std::pair<std::string, std::vector<int>> // The dataset is represented as a vector of these columns // Note that all columns should exist the same size // Create an output filestream object std :: ofstream myFile ( filename ); // Ship cavalcade names to the stream for ( int j = 0 ; j < dataset . size (); ++ j ) { myFile << dataset . at ( j ). beginning ; if ( j != dataset . size () - 1 ) myFile << "," ; // No comma at end of line } myFile << " \n " ; // Ship data to the stream for ( int i = 0 ; i < dataset . at ( 0 ). 2d . size (); ++ i ) { for ( int j = 0 ; j < dataset . size (); ++ j ) { myFile << dataset . at ( j ). 2nd . at ( i ); if ( j != dataset . size () - 1 ) myFile << "," ; // No comma at end of line } myFile << " \n " ; } // Close the file myFile . shut (); } int master () { // Make three vectors, each of length 100 filled with 1s, 2s, and 3s std :: vector < int > vec1 ( 100 , one ); std :: vector < int > vec2 ( 100 , 2 ); std :: vector < int > vec3 ( 100 , 3 ); // Wrap into a vector std :: vector < std :: pair < std :: string , std :: vector < int >>> vals = {{ "One" , vec1 }, { "Ii" , vec2 }, { "Three" , vec3 }}; // Write the vector to CSV write_csv ( "three_cols.csv" , vals ); return 0 ; }
Hither we've represented each column of data equally a std::pair
of <column name, column values>
, and the whole dataset equally a std::vector
of such columns. Now we tin can write a variable number of integer columns to a CSV file.
Reading from CSV
Now that we've written some CSV files, let's attempt to read them. For now permit'due south correctly assume that our file contains integer data plus ane row of column names at the top.
#include <string> #include <fstream> #include <vector> #include <utility> // std::pair #include <stdexcept> // std::runtime_error #include <sstream> // std::stringstream std :: vector < std :: pair < std :: string , std :: vector < int >>> read_csv ( std :: string filename ){ // Reads a CSV file into a vector of <string, vector<int>> pairs where // each pair represents <column proper name, column values> // Create a vector of <string, int vector> pairs to shop the result std :: vector < std :: pair < std :: cord , std :: vector < int >>> result ; // Create an input filestream std :: ifstream myFile ( filename ); // Make sure the file is open up if ( ! myFile . is_open ()) throw std :: runtime_error ( "Could not open up file" ); // Helper vars std :: string line , colname ; int val ; // Read the column names if ( myFile . good ()) { // Extract the first line in the file std :: getline ( myFile , line ); // Create a stringstream from line std :: stringstream ss ( line ); // Extract each cavalcade name while ( std :: getline ( ss , colname , ',' )){ // Initialize and add <colname, int vector> pairs to event event . push_back ({ colname , std :: vector < int > {}}); } } // Read data, line by line while ( std :: getline ( myFile , line )) { // Create a stringstream of the current line std :: stringstream ss ( line ); // Keep track of the current column index int colIdx = 0 ; // Extract each integer while ( ss >> val ){ // Add together the current integer to the 'colIdx' column's values vector result . at ( colIdx ). second . push_back ( val ); // If the adjacent token is a comma, ignore it and motion on if ( ss . peek () == ',' ) ss . ignore (); // Increment the cavalcade index colIdx ++ ; } } // Close file myFile . shut (); return effect ; } int main () { // Read three_cols.csv and ones.csv std :: vector < std :: pair < std :: string , std :: vector < int >>> three_cols = read_csv ( "three_cols.csv" ); std :: vector < std :: pair < std :: string , std :: vector < int >>> ones = read_csv ( "ones.csv" ); // Write to another file to check that this was successful write_csv ( "three_cols_copy.csv" , three_cols ); write_csv ( "ones_copy.csv" , ones ); return 0 ; }
This program reads our previously created CSV files and writes each dataset to a new file, essentially creating copies of our original files.
Going further
And then far we've seen how to read and write datasets with integer values but. Extending this to read/write a dataset of only doubles or only strings should be fairly straight-forward. Reading a dataset with unknown, mixed information types is another animal and beyond the scope of this article, but encounter this code review for possible solutions.
Special thanks to papagaga and Incomputable for helping me with this topic via codereview.stackexchange.com.
cartwrightarage1945.blogspot.com
Source: https://www.gormanalysis.com/blog/reading-and-writing-csv-files-with-cpp/
0 Response to "Read a Line From File and Push Into Vector in C++"
Postar um comentário