I am trying to write C++ code that opens a csv file and reads multiple inputs from one line. So the data type format of the csv file is:
int, string, int, int
What I want to do is read all of those into a variable at once like so
ifstream myfile;
myfile.open("input.csv");
string a;
int b, c, d;
while (myfile.is_open() && myfile.good())
{
if(myfile >> b >> a >> c >> d)
cout << a << " " << b << " " << c << " " << d << " " ;
myfile.close();
}
But when I run my code, it just skips the if line and goes to the .close() line. Nothing is being printed out. I think it is unable to read those values in.
What is wrong with my code? Why can't it read those values?
Do something line following to extract token from a properly formatted csv file.
#include <sstream>
// ....
std::string line ;
while ( std::getline( myfile, line ) )
{
std::stringstream buffer( line );
std::string token;
while( std::getline( buffer, token, ',' ) )
{
// std::cout << token << std::endl;
// convert to int, etc
}
}
But when I run my code, it just skips the if line and goes to the .close() line. Nothing is being printed out. I think it is unable to read those values in.
That's because there is an error in reading the second field from the CSV file. You haven't done anything in your code to skip the comma (,) while reading the data.
You can use different strategies to read the data in. My suggestion:
Read the file line by line.
Divide the line into tokens using ',' as the separator.
Convert each token to the data you expect to see using std::istringstream.
Related
Lets say I want to input the hours, minutes and seconds from the first line of a file and store them to 3 different variables, hrs, mins and sec respectively.
I cant figure out an easy way to skip reading the colon character (":").
Input file example:
12:49:00
Store:
hrs = 12
mins = 59
sec = 00
You can use std::regex to match, range-check and validate your input all at once.
#include <iostream>
#include <regex>
#include <string>
int main()
{
const std::regex time_regex("(\\d|[0,1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)");
std::smatch time_match;
std::string line;
while (std::getline(std::cin, line))
{
if (std::regex_match(line, time_match, time_regex))
{
int hours = std::stoi(time_match[1]);
int minutes = std::stoi(time_match[2]);
int seconds = std::stoi(time_match[3]);
std::cout << "h=" << hours << " m=" << minutes << " s=" << seconds << std::endl;
}
else
{
std::cout << "Invalid time: " << line << std::endl;
}
}
return 0;
}
See this example live here.
Breaking down the regular expression (\\d|[0,1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d):
\d|[0,1]\d|2[0-3] matches the hour (24-hour time) which is one of:
\d : 0-9
[0,1]\d : 01-19
2[0-3] : 20-23
[0-5]\d matches the minutes: two digits 00-59
[0-5]\d matches the seconds: two digits 00-59, as above.
An alternative not using a temporary character for skipping the colon:
#include <iostream>
int main()
{
int h,m,s;
std::cin >> h;
std::cin.ignore(1) >> m;
std::cin.ignore(1) >> s;
std::cout << h << ':' << m << ':' << s << std::endl;
return 0;
}
This seems to work:
int h, m, s;
char c;
cin >> h >> c >> m >> c >> s;
You just skip : symbol this way. I don't know whether it's a good solution.
With cin.ignore:
cin >> h;
cin.ignore(1);
cin >> m;
cin.ignore(1);
cin >> s;
There are already several good answers and one that has already been accepted; however I like to propose my solution not only as a valid answer to your problem but also in regards to a good design practice. IMHO when it involves reading information from a file and storing it's contents to variables or data structures I prefer to do it in a specific way. I like to separate the functionality and responsibility of specific operations into their own functions:
1: I first like to have a function to open a file, read the contents and to store the information into either a string, a stream or some large buffer. Once the appropriate amount of information is read from the file, then the function will close the file handle as we are done with it and then return back the results. There are several ways to do this yet they are all similar.
a: Read a single line from the file and return back a string or a stream.
b: Read in all information form the file line by line and store each line into its own string or stream and return back a vector of those strings or streams.
c: Read in all of the contents of the file into a single string, stream or large buffer and return that back.
2: After I have the contents of that file then I will typically call a function that will parse that data and these functions will vary depending on the type of content that needs to be parsed based on the data structures that will be used. Also, these parsing functions will call a function that will split the string into a vector of strings called tokens. After the split string function is called then the parsing of data will use the string manipulators-converters to convert a string to the required built in types that are needed for the current data structure that is in use and store them into the data structure that is passed in by reference.
3: There are two variations of my splitString function.
a: One takes a single character as a delimiter.
b: The other will take a string as its delimiter.
c: Both functions will return a vector of strings, based on the delimiter used.
Here is an example of my code using this text file for input.
time.txt
4:32:52
main.cpp
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <exception>
struct Time {
int hours;
int minutes;
int seconds;
};
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
std::string getLineFromFile( const char* filename ) {
std::ifstream file( filename );
if( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
std::getline( file, line );
file.close();
return line;
}
void parseLine( const std::string& fileContents, Time& time ) {
std::vector<std::string> output = splitString( fileContents, ':' );
// This is where you would want to do your sanity check to make sure
// that the contents from the file are valid inputs before converting
// them to the appropriate types and storing them into your data structure.
time.hours = std::stoi( output.at( 0 ) );
time.minutes = std::stoi( output.at( 1 ) );
time.seconds = std::stoi( output.at( 2 ) );
}
int main() {
try {
Time t;
std::string line = getLineFromFile( "time.txt" );
parseLine( line, t );
std::cout << "Hours: " << t.hours << '\n'
<< "Minutes: " << t.minutes << '\n'
<< "Seconds: " << t.seconds << "\n\n";
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Output:
Hours: 4
Minutes: 32
Seconds: 52
Now as you can see in this particular situation the functions that are being used here is designed only to read a single line from the file and of course the very first line from the file. I have other functions in my library not shown here that will read each line of a file until there are no more lines to read, or read all of the file into a single buffer. I have another version of split string that will take a string as its delimiter instead of a single character. Finally for the parsing function, each parsing function will end up being unique due to the fact that it will rely on the data structure that you are trying to use.
This allows the code to be readable as each function does what it is supposed to do and nothing more. I prefer this design over the fact of trying to get information from a file and trying to parse it while the file is open. Too many things can go wrong while the file is open and if the data is read wrong or corrupted but to the point where the compiler doesn't complain about it, then your variables or data structures may contain invalid information without you being aware of it. At least in this way you can open the file, get what you need from the file and store it into a string or a vector of strings, close the file when done reading and return back the contents. Then it becomes the parsing function's responsibility to test the data after it has been tokenized. Now, in the current parsing function that I shown above I did not do any sanity check to keep things simple, but that is where you would test your data to see if the information is valid before returning back your populated data structure.
If you are interested in another version of this where there are multiple lines being read in from the file, just comment a request and I will append it to this answer.
I'm having trouble figuring out how to get newlines to display when outputting text to cmdPrompt. The .text file is something like this:
"Roses are red
violets are blue
sugar is sweet
and so are you"
And my code for the loop is:
#define newLn "\n"
ifstream ins; //these two are near the top where the programs opens the file
string aString;
while(ins >> aString){
if(aString != newLn){
cout << aString << ' ';
}
else
cout << endl;
}
It reads in the text fine but it just displays it like this:
Roses are red violets are blue sugar is sweet and so are you
I don't know how to display it exactly like it is in the text file (with the newlines after each statement. I know you can just do while(nextCharacter != newLn) for reading in by chars but strings got me stumped.
When you use formatted extraction functions, such as:
while(ins >> aString){
you lose all the whitespace characters that are present in the stream.
In order to preserve the whitespaces, you can use std::getline.
std::string line;
while ( getline(ins, line) )
{
std::cout << line << std::endl;
}
If you need to extract the individual tokens from the lines, you can process the lines of text using std::istringstream.
std::string line;
while ( getline(ins, line) )
{
cout << line << std::endl;
std::istringstream str(line);
std::string token;
while ( str >> token )
{
// Use token
}
}
You are using the "fstream extraction operator" to read in the file content. So keep in mind the operator doesn't read take in account white spaces and new lines but it consider them to be the end of the word. So instead use std::getline.
while(std::getline(ins, aString) )
std::cout << aString << std::endl;
I want to read data from a file with a quite strange structure. The file looks like this below:
some lines with text....
10 1000 10
1 1 1
1 100 100
.
.
.
some lines with text...
again data like above..
some lines with text... etc
So I have two questions:
How can I read only the specific lines with the data?
How can I read these right aligned data?
Here is one of my trials:
string line;
ifstream myfile ("aa.txt");
double a,b,c;
while (! myfile.eof() )
{
for (int lineno = 0; getline (myfile,line); lineno++)
if (lineno>2 && lineno<5){
myfile>>a>>b>>c;
cout<<lineno<<" " << line << endl;}
}
myfile.close();
how can I read only the specific lines with the data?
well, read all the lines, and then write a function to detect whether the current line is a "data" one or not.
What are the characteristics of your data line? It consists only of digits and spaces? Could there be tabs? What about the columns, are they fixed width? Your predicate function can check there are spaces in the required columns if so.
how can I read these right aligned data?
You want to extract the integer values? Well, you can create a std::istringstream for your line (once you've checked it is data), and then use the >> stream extraction operator to read values into variables of the appropriate type.
Read up on how it handles whitespace (and/or experiment) - it might just do what you need with no effort.
this is just a simple example: you declare 3 variables as you did a, b , c as integer and a string line you open a file and input line convert line to integer if ok assign it to a if not don't do anything to b and c until next read until a valid conversion for a is ok then input b and c and like this:
#include <iostream>
#include <string>
#include <fstream>
int main()
{
std::ifstream in("data.txt");
int a = 0, b = 0, c = 0;
std::string sLine;
if(in.is_open())
{
while(in >> sLine)
{
if( a = atoi(sLine.c_str()))
std::cout << "a: " << a << std::endl;
if(a)
{
in >> sLine;
if( b = atoi(sLine.c_str()))
std::cout << "b: " << b << std::endl;
}
if(b)
{
in >> sLine;
if( c = atoi(sLine.c_str()))
std::cout << "c: " << c << std::endl;
}
}
}
in.close();
std::cout << std::endl;
return 0;
}
I have a CSV file in the form of two columns: name, age
To read and store the info, I did this
struct person
{
string name;
int age;
}
person record[10];
ifstream read("....file.csv");
However, when I did
read >> record[0].name;
read.get();
read >> record[0].age;
read>>name gave me the whole line instead of just the name. How could I possibly avoid this problem so that I can read the integer into age?
Thank you!
You can first read the whole line with std:getline, then parse it via a std::istringstream (must #include <sstream>), like
std::string line;
while (std::getline(read, line)) // read whole line into line
{
std::istringstream iss(line); // string stream
std::getline(iss, record[0].name, ','); // read first part up to comma, ignore the comma
iss >> record[0].age; // read the second part
}
Below is a fully working general example that tokenizes a CSV file Live on Ideone
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
int main()
{
// in your case you'll have a file
// std::ifstream ifile("input.txt");
std::stringstream ifile("User1, 21, 70\nUser2, 25,68");
std::string line; // we read the full line here
while (std::getline(ifile, line)) // read the current line
{
std::istringstream iss{line}; // construct a string stream from line
// read the tokens from current line separated by comma
std::vector<std::string> tokens; // here we store the tokens
std::string token; // current token
while (std::getline(iss, token, ','))
{
tokens.push_back(token); // add the token to the vector
}
// we can now process the tokens
// first display them
std::cout << "Tokenized line: ";
for (const auto& elem : tokens)
std::cout << "[" << elem << "]";
std::cout << std::endl;
// map the tokens into our variables, this applies to your scenario
std::string name = tokens[0]; // first is a string, no need for further processing
int age = std::stoi(tokens[1]); // second is an int, convert it
int height = std::stoi(tokens[2]); // same for third
std::cout << "Processed tokens: " << std::endl;
std::cout << "\t Name: " << name << std::endl;
std::cout << "\t Age: " << age << std::endl;
std::cout << "\t Height: " << height << std::endl;
}
}
read>>name gave me the whole line instead of just the name. How could I possibly avoid this problem so that I can read the integer into age?
read >> name will read everything into name until a white space is encountered.
If you have a comma separated line without white spaces, it makes sense that the entire line is read into name.
You can use std::getline to read the entire line to one string. Then use various methods of tokenizing a std::string.
Sample SO posts that address tokenizing a std::string:
How do I tokenize a string in C++?
c++ tokenize std string
Splitting a C++ std::string using tokens, e.g. ";"
You maybe could use stringstreams for that, but I wouldn't trust this, if I'm honest.
If I was you, I would write a small function, that reads the whole line into a string and after that, it should search for the separator character in the string. Everything in front of that is the first column and everything behind the second one. With the string operations provided by C++ you can move these parts in your variables (you can convert them into the correct type if you need).
I wrote a small C++ Library for CSV parsing, maybe a look at it helps you. You can find it on GitHub.
EDIT:
In this Gist you can find the parsing function
I'm just learning about text file input/output. I have outputted a file which contains a header and 10 rows of data underneath it.
I now want to read this back to the main function. This works for me if I leave out the header in the text file, but if I leave the header in, I get an infinite loop.
How can I skip the 1st line (the header line) in reading this data back, or if possible, read back the header as well as the data?
Here is what I have so far:
void fileRead(int x2[], double y2[], int& n, char filename)
{
ifstream fin ("pendulum.txt"); // fin is an input file stream
if(!fin) //same as fin.fail()
{
cerr << "Failure to open pendulum.txt for input" << endl;
exit(1);
}
int j = 0, dummy = 0; //index of the first value j and dummy value
while(!fin.eof()) //loop while not end of file
{
fin >> dummy >> x2[j] >> y2[j];
cout << setw(5) << fixed << j
<< setw(12) << scientific << x2[j] << " "
<< setw(12) << y2[j] << endl; //print a copy on screen
j += 1;
}
fin.close(); //close the input file
}
You can first read the header of the file then the real contents you want as follows:
string line;
getline(fin, line);//just skip the line contents if you do not want header
while (fin >> dummy >> x2[j] >> y2[j] )
{ //^^if you do not always have a dummy at the beginning of line
//you can remove dummy when you read the rest of the file
//do something
}
Your best bet would be to use
fin.ignore(10000,'\n');
http://www.cplusplus.com/reference/istream/istream/ignore/
This will ignore the first 10000 character in the file, or ignore the characters until a newline is reached. The 10000 is fairly arbitrary and should be a number that will always be longer than the maximum line length.
man, this gentleman over there helped me quite a lot. You see, everyone says to use getline(); to skip one line, but the problem is that sometimes you dont want to store anything in a buffer, so ignore() makes much more sense to me. Well so I would like to back up our fella's answer by adding that, you could use " numeric_limits::max()" which will make it have no limit, it will ignore until it finds the delimiter...
`
#include <iostream>
#include <fstream>
#include <limits>
using std::streamsize;
int main() {
ifstream fin ("pendulum.txt");
fin.ignore(numeric_limits<streamsize>::max(),'\n');
}
`
http://www.cplusplus.com/reference/limits/numeric_limits/