comma separated stream into struct - c++

I have a structure with an int and two strings. When reading in the file it is comma seperated for the first two values and the last value is terminated by a newline. The third argument could be empty however.
ex data: 7, john doe, 123-456-7891 123 fake st.
I want to make it so that my program will grab the first number and put it in the int, find the comma and put the second number in the struct's string etc.
First question is should I use a class instead? I have seen the getline(stream, myString, ','); but my arguments are different data types so I can't just throw them all into a vector.
my code:
struct Person{
int id;//dont care if this is unique
string name;
string extraInfo;
};
int main(int argc, char* argv[]){
assert( argc ==2 && "Invalid number of command line arguments");
ifstream inputFile (argv[1]);
assert( inputFile.is_open() && "Unable to open file");
}
What is the best way of storing this information and retrieving it from a file that is comma separated for the first two and ends with a newline? I also want the program to ignore blank lines in the file.

I'd read the file line-by-line using normal getline(). Then, put it into a stringstream for further parsing or use string's find() functions to split the text manually.
Some more notes:
I don't understand your first question about using a class. If you mean for Person, then the answer is that it doesn't matter.
Using assert for something you don't have control over is wrong, like argc. This should only be used to verify that you didn't make a programming error. Also, if you #define NDEBUG, the asserts are all gone, so they shouldn't really be part of your program logic. Throw std::runtime_error("failed to open file") instead.
You probably don't want the double quotes in your strings. Also, you might want "a,b" to not be split by the comma. Make sure you have tests that assert the required functionality.

You can still use the getline approach for tokenising a line, but you first have to read the line:
vector<Person> people;
string line;
int lineNum = 0;
while( getline(inputFile, line) )
{
istringstream iss(line);
lineNum++;
// Try to extract person data from the line. If successful, ok will be true.
Person p;
bool ok = false;
do {
string val;
if( !getline(iss, val, ',') ) break;
p.id = strtol( val.c_str(), NULL, 10 );
if( !getline(iss, p.name, ',') ) break;
if( !getline(iss, p.extraInfo, ',') ) break;
// Now you can trim the name and extraInfo strings to remove spaces and quotes
//[todo]
ok = true;
} while(false);
// If all is well, add the person to our people-vector.
if( ok ) {
people.push_back(p);
} else {
cout << "Failed to parse line " << lineNum << ": " << line << endl;
}
}

Once you get the line in string using getline, use strtok.
char myline[] = "7, john doe, 123-456-7891 123 fake st.";
char tokens = strtok(myline, ",");
while(tokens)
{
//store tokens in your struct values here
}
You'll need to include #include <string.h> to use strtok

Related

Reading text with blanks and numeric data from a file

So I have data in a text like this:
Alaska 200 500
New Jersey 400 300
.
.
And I am using ifstream to open it.
This is part of a course assignment. We are not allowed to read in the whole line all at once and parse it into the various pieces. So trying to figure out how to read each part of every line.
Using >> will only read in "New" for "New Jersey" due to the white space/blank in the middle of that state name. Have tried a number of different things like .get(), .read(), .getline(). I have not been able to get the whole state name read in, and then read in the remainder of the numeric data for a given line.
I am wondering whether it is possible to read the whole line directly into a structure. Of course, structure is a new thing we are learning...
Any suggestions?
Can't you just read the state name in a loop?
Read a string from cin: if the first character of the string is numeric then you've reached the next field and you can exit the loop. Otherwise just append it to the state name and loop again.
Here is a line by line parsing solution that doesn't use any c-style parsing methods:
std::string line;
while (getline(ss, line) && !line.empty()) {
size_t startOfNumbers = line.find_first_of("0123456789");
size_t endOfName = line.find_last_not_of(" ", startOfNumbers);
std::string name = line.substr(0, endOfName); // Extract name
std::stringstream nums(line.substr(startOfNumbers)); // Get rest of the line
int num1, num2;
nums >> num1 >> num2; // Read numbers
std::cout << name << " " << num1 << " " << num2 << std::endl;
}
If you can't use getline, do it yourself: Read and store in a buffer until you find '\n'. In this case you probably also cannot use all the groovy stuff in std::string and algorithm and might as well use good ol' C programming at that point.
Once you have grabbed a line, read your way backwards from the end of the line and
Discard all whitespace until you find non whitespace.
Gather characters found into token 3 until you find whitepace again.
Read and discard the whitespace until you find the end of token 2.
Gather token 2 until you find more whitespace.
Discard the whitespace until you find the end of token 1. The rest of the line is all token 1.
convert token 2 and token 3 into numbers. I like to use strtol for this.
You can build all of the above or Daniel's answer (use his answer if at all possible) into an overload of operator>>. This lets you
mystruct temp;
while (filein >> temp)
{
// do something with temp. Stick it in a vector, whatever
}
The code to do this looks something like (Stealing wholesale from What are the basic rules and idioms for operator overloading? <-- Read this. It could save your life one day)
std::istream& operator>>(std::istream& is, mystruct & obj)
{
// read obj from stream
if( /* no valid object of T found in stream */ )
is.setstate(std::ios::failbit);
return is;
}
Here's another example of reading the file word by word. Edited to remove the example using the eof check as the while loop condition. Also included a struct as you mentioned that's what you just learned. I'm not sure how you're supposed to use your struct, so I just made it simple and had it contain 3 variables, a string, and 2 ints. To verify it reads correctly it couts the contents of the struct variables after its read in which includes printing out "New Jersey" as one word.
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h> // for atoi
using namespace std;
// Not sure how you're supposed to use the struct you mentioned. But for this example it'll just contain 3 variables to store the data read in from each line
struct tempVariables
{
std::string state;
int number1;
int number2;
};
// This will read the set of characters and return true if its a number, or false if its just string text
bool is_number(const std::string& s)
{
return !s.empty() && s.find_first_not_of("0123456789") == std::string::npos;
}
int main()
{
tempVariables temp;
ifstream file;
file.open("readme.txt");
std::string word;
std::string state;
bool stateComplete = false;
bool num1Read = false;
bool num2Read = false;
if(file.is_open())
{
while (file >> word)
{
// Check if text read in is a number or not
if(is_number(word))
{
// Here set the word (which is the number) to an int that is part of your struct
if(!num1Read)
{
// if code gets here we know it finished reading the "string text" of the line
stateComplete = true;
temp.number1 = atoi(word.c_str());
num1Read = true; // won't read the next text in to number1 var until after it reads a state again on next line
}
else if(!num2Read)
{
temp.number2 = atoi(word.c_str());
num2Read = true; // won't read the next text in to number2 var until after it reads a state agaon on next line
}
}
else
{
// reads in the state text
temp.state = temp.state + word + " ";
}
if(stateComplete)
{
cout<<"State is: " << temp.state <<endl;
temp.state = "";
stateComplete = false;
}
if(num1Read && num2Read)
{
cout<<"num 1: "<<temp.number1<<endl;
cout<<"num 2: "<<temp.number2<<endl;
num1Read = false;
num2Read = false;
}
}
}
return 0;
}

How to add comma separated list of double values into a vector? [duplicate]

I have a structure with an int and two strings. When reading in the file it is comma seperated for the first two values and the last value is terminated by a newline. The third argument could be empty however.
ex data: 7, john doe, 123-456-7891 123 fake st.
I want to make it so that my program will grab the first number and put it in the int, find the comma and put the second number in the struct's string etc.
First question is should I use a class instead? I have seen the getline(stream, myString, ','); but my arguments are different data types so I can't just throw them all into a vector.
my code:
struct Person{
int id;//dont care if this is unique
string name;
string extraInfo;
};
int main(int argc, char* argv[]){
assert( argc ==2 && "Invalid number of command line arguments");
ifstream inputFile (argv[1]);
assert( inputFile.is_open() && "Unable to open file");
}
What is the best way of storing this information and retrieving it from a file that is comma separated for the first two and ends with a newline? I also want the program to ignore blank lines in the file.
I'd read the file line-by-line using normal getline(). Then, put it into a stringstream for further parsing or use string's find() functions to split the text manually.
Some more notes:
I don't understand your first question about using a class. If you mean for Person, then the answer is that it doesn't matter.
Using assert for something you don't have control over is wrong, like argc. This should only be used to verify that you didn't make a programming error. Also, if you #define NDEBUG, the asserts are all gone, so they shouldn't really be part of your program logic. Throw std::runtime_error("failed to open file") instead.
You probably don't want the double quotes in your strings. Also, you might want "a,b" to not be split by the comma. Make sure you have tests that assert the required functionality.
You can still use the getline approach for tokenising a line, but you first have to read the line:
vector<Person> people;
string line;
int lineNum = 0;
while( getline(inputFile, line) )
{
istringstream iss(line);
lineNum++;
// Try to extract person data from the line. If successful, ok will be true.
Person p;
bool ok = false;
do {
string val;
if( !getline(iss, val, ',') ) break;
p.id = strtol( val.c_str(), NULL, 10 );
if( !getline(iss, p.name, ',') ) break;
if( !getline(iss, p.extraInfo, ',') ) break;
// Now you can trim the name and extraInfo strings to remove spaces and quotes
//[todo]
ok = true;
} while(false);
// If all is well, add the person to our people-vector.
if( ok ) {
people.push_back(p);
} else {
cout << "Failed to parse line " << lineNum << ": " << line << endl;
}
}
Once you get the line in string using getline, use strtok.
char myline[] = "7, john doe, 123-456-7891 123 fake st.";
char tokens = strtok(myline, ",");
while(tokens)
{
//store tokens in your struct values here
}
You'll need to include #include <string.h> to use strtok

Read comma separated values with stray whitespaces from a textfile in c++

I have a file that contains string,int,int values in multiple lines.
Delhi,12,13
Mumbai,100 , 101
Kolkata,11, 12
The values are separated by commas but there can be stray whitespaces in between.My current code is this :
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
int main()
{
FILE *f = fopen("input.txt","r");
int lines = 0;
char c = getc(f);
while(c != EOF)
{
if(c == '\n')
{
lines++;
}
c = getc(f);
}
lines++;
string arr[lines];
int t1[lines];
int t2[lines];
char s1[100],s2[100],s3[100];
int x,y;
fclose(f);
f = fopen("input.txt","r");
while (fscanf(f,"%99[^,],%99[^,],%99[^,]", s1, s1, s2)==3)
{
cout << s1 << s2 << s3 << endl;
}
}
This doesn't seem to quite properly read the values and display on the screen first of all. How do I read the string and the integer values here(which may have stray whitespaces) and store them into an array (three arrays to be precise) ?
Try doing this:
fscanf(f,"%[^, ]%*[ ,]%d%*[ ,]%d ", s1, &x, &y);
%[^, ] => searches for everything except , and <space> and stores it in s1
%*[ ,] => searches for , and <space> but does not store it anywhere (the * ensures that)
%d => stores the number
The problem is on this line:
while (fscanf(f,"%99[^,],%99[^,],%99[^,]", s1, s1, s2)==3)
It tries to scan up to the next comma character ',', which occurs on the next line. Replace with %99[^\n] to fix this problem:
while (fscanf(f,"%99[^,],%99[^,],%99[^\n]", s1, s1, s2)==3)
Why are you using FILE* and friends in C++?
The other answers specify the problem with your code, so I'm writing this answer to show you how to improve it.
std::ifstream file("input.txt");
std::string name, value0, value1;
while (std::getline(file, name, ',')) {
// Get the value strings from the stream.
std::getline(file, value0, ',');
std::getline(file, value1, ',');
// These will throw an exception when given invalid input.
int v0 = std::stoi(value0);
int v1 = std::stoi(value1);
// Do stuff with the strings
}
std::getline can be used to extract a string from a stream up until a certain delimiter. Whitespaces are ignored here, so we don't have to care about them. The return value of std::getline is the stream passed in, and it has an operator bool() that allows us to use it as a boolean expression. The value will become false when the stream is either empty or in some erroneous state.
Note that the above should be similar in behavior to:
while (file) {
std::getline(file, name, ',');
// ...
}
I'm pretty sure this must be a whole lot more readable than a string like "%99[^,],%99[^,],%99[^,]".
Cheers~

Parsing a string of numbers into an integer array

I have a text file with numbers ranging from 0-255 separated by commas. I want to be able to store each of these numbers into an integer array. An example of what the text file might contain is;
"32,51,45,12,5,2,7,2,9,233,132,175,143,33..." etc
I have managed to get my program to store the data from the text file as a string and output them on the screen. What I need to do next is store the values of that string in an integer array, separating the numbers by the commas.
Here is the code I have written so far, which I am having problems getting it working;
int _tmain(int argc, _TCHAR* argv[])
{
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
//STRING CONVERSION
std::string str = line;
std::vector<int> vect;
std::stringstream ss(str);
int i = 0;
while (ss >> i)
{
vect.push_back(i);
if (ss.peek() == ',')
ss.ignore();
}
system("pause");
return 0;
It looks like your code for tokenizing your string is bit off. In particular you need to make sure you call atoi() on the string of your integer to get an integer. I'll focus on the parsing of the string though.
One thing you could use is C's strtok. I recommend this mainly because your case is rather simple, and this is probably the simplest way to go about it.
The code you'd look for is essentially this:
char* numStr = strtok(str.c_str(), ",");
while (numStr)
{
vect.push_back(atoi(numStr));
numStr = strtok(NULL, ",");
}
strtok() takes two arguments: a pointer to the C-style string (char*) you're tokenizing, and the string of delimiters (note that each character in the delimiter string is treated as its own delimiter).
I should mention that strtok is not thread-safe, and you also have to ensure that the string you extract from the file ends with a null character \0.
The answers to this question provide many alternatives to my solution. If you'd prefer to use std::stringstream then I suggest you look at the 5th answer on that page.
Regarding your trouble with PDBs, what is the exact error you're getting?

String is not printing without new line character in C++

I'm opening a file, and getting lines from it.
The first line should say how many variables there are, and what their names are.
The second line should be a logic equation using these variables.
The assignment is to have it print out a truth table for the variables and equation.
The first line the program is taking in is not printing without me inserting a new line character. I tried converting to a string and using both printf and cout.
Main file that inputs everything:
#include "truthTable2.h"
int main(int argc, const char* argv[]){
ifstream inFile;
if(argc != 2){
cout << "Enter an input file name: ";
char *inFileName = "";
cin >> inFileName;
inFile.open(inFileName);
}
else
inFile.open(argv[1]);
TruthTable tTable;
while(!inFile.eof()){
char variableLine[256];
inFile.getline(variableLine, 256);
printf("%s ", variableLine);
string variable(variableLine);
tTable.setVariables(variable);
char formulaLine[256];
inFile.getline(formulaLine, 256);
cout << formulaLine << "\n";
string formula(formulaLine);
tTable.setFormula(formula);
tTable.printTable();
}
inFile.close();
return 0;
}
Sample input:
2 x y
( \wedge x ( \not y ) )
Output from this:
( \wedge x ( \not y ) )
I think whatever is causing this is giving me problems throughout the rest of the program as well. After I tokenize the variableLine it does not print without a new line character and it does not find the second variable when evaluating the formula.
An std::ostream's output needs to be flushed. It is normally flushed automatically when a line-feed \n is written. If you want to force the stream to flush, you can use the std::flush manipulator like so:
std::cout << "foo" << std::flush;
Edit: Although my post clearly answers the question "Why does my line not show up unless I output a \n character?" You said this does not answer your question, so I will attempt some mind reading to try and answer your real question.
Since I have no idea what you really want know, I'll point out several things here that are wrong with your code and it might help you find your problem or clarify your question.
First, if you are using the file name input from std::cin, when argc<2, you will, a 100% guaranteed, cause a failure in your application. The reason is that the character buffer pointed to by inFileName contains a single byte, reserved for the terminating null character. If someone enters any text whatsoever, you will get a buffer overrun. If someone enters an empty string, your program will open no file and inFile.open(...); will return an error code that you don't check, so your program won't crash, but still won't work.
Second, the other line inputs are needlessly limited to 256 characters and are just as dangerous (i.e. lines longer that 256 characters will cause a bufer overrun). Since you eventually create std::string instances out of the content, you should just plainly use std::getline(). It is shorter to type, more general and safer.
Third, the description of your problem is that no output is generated unless you add a \n character. As I explained, this is perfectly normal. From re-reading your post, I can understand that you don't unhderstand why you should have to add one given that there was already one in the input file. The reason you need to add it is because the getline() functions discard the \n character. It is not inserted into your line's buffer.
I've cleaned up some of your code to show you some clear improvements. From this code you will be able to understand the structure of your program, which should also reflect the structure of your input.
#include "truthTable2.h"
int main(int argc, const char* argv[]){
std::ifstream inFile;
if(argc != 2){
cout << "Enter an input file name: ";
std::string inFileName;
std::getline(std::cin, inFileName);
inFile.open(inFileName.c_str());
}
else {
inFile.open(argv[1]);
}
if ( !inFile.is_open() ) {
// Did not successfully open a file. Print error message and exit!
}
TruthTable tTable;
for (std::string variables; std::getline(inFile,variables); )
{
std::cout << variables << std::endl;
tTable.setVariables(variable);
std::string formula std::getline(formula);
std::cout << formula << std::endl;
tTable.setFormula(formula);
tTable.printTable();
}
return 0;
}
From this, I have a question:how is your input structured? Is your input file only consisted of 2 lines? Are there multiple sets of these line pairs? Is there a single line with variables and a bunch of equations? These three cases will lead me to re-structure the program in one of the following fashions:
2 lines only:
ThruthTable table;
std::string variables, equation;
std::getline(file, variables);
std::getline(file, equation);
// ...
Multiple sets:
while ( !inFile.eof() )
{
ThruthTable table;
std::string variables, equation;
std::getline(file, variables);
std::getline(file, equation);
// ...
}
Multiple equations:
ThruthTable table;
std::string variables;
std::getline(variables);
for ( std::string equation; std::getline(file, equation); )
{
std::getline(file, equation);
// ...
}
If what I am seeing is right, the output from printf is the one that is not showing. In that case, either use
fflush(stdout);
Or better, just go with a std::cout for that line since you're writing it in C++ (using the std::flush technique, of course.)