Parsing a string of numbers into an integer array - c++

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?

Related

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

How to find a string of 2 words in a file?

With the following code, I can find a string of 1 word (in this example I'm looking for "Word"):
ifstream file("file.txt");
string str;
while (file >> str){
if (str.find("Word") != string::npos){
////
}
}
But it doesn't work if I want to find, for example, "Computer screen", which is composed of two words.
Thanks
file >> str reads a parameter (in this case, a string) delimited with whitespace. If you want to read the whole line (or in any case, more than one word at once), you can use getline operator (reads the string which is delimited by newline by default).
ifstream file("file.txt");
string str;
while (std::getline (file,str)){
if (str.find("Computer screen") != string::npos){
////
}
}
If you know there are two words and what they are, all you need is this:
ifstream file("file.txt");
string str;
while (file >> str){
if (str.find("Computer") != string::npos){
file >> str;
if (str.find("screen") != string::npos) {
////
}
}
}
But more likely, you are asking to find a single string that might be two words, or three or more.
Then, can you count on the string being on a single line? In which case, #Ashalynd's solution will work. But if the string might be broken it will fail. You then need to handle that case.
If your file is "small" - i.e. can easily fit in memory, read in the whole thing, remove line breaks and search for the string.
If it is too large, read in lines as pairs.
Something like this:
std::ifstream file("file.txt");
std::string str[2];
int i = 0;
std::getline (file,str[i]);
++i;
while (std::getline (file,str[i]))
{
int next_i = (i+1)%2;
std::string pair = str[next_i] + " " + str[i];
if (pair.find("Computer screen") != std::string::npos)
{
////
}
i = next_i;
}
All this assumes that the possible white space between the words in the string is a single space or a newline. If there is a line break with more white-space of some kind (e.g. tabs, you need either to replace white-space in the search string with a regex for white-space, or implement a more complex state machine.
Also, consider whether you need to manage case, probably by converting all strings to lower case before the match.

comma separated stream into struct

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

Why is this IO operation looping infinitely?

I am trying to read from a text file and tokenize the input. I was getting a segmentation fault until I realized I forgot to close my ifstream. I added the close call and now it loops infinitely. I'm just trying to learn how to use strtok for now, that is why the code doesn't really look complete.
void loadInstructions(char* fileName)
{
ifstream input;
input.open(fileName);
while(!input.eof());
{
string line;
getline (input,line);
char * lineChar = &line[0];
//instruction cmd; //This will be used later to store instructions from the parse
char * token;
token = strtok (lineChar," ");
// just trying to get the line number for now
int lineNumber = atoi(token);
cout << lineNumber << "\n";
}
input.close();
}
input file:(one line)
5 +8 0 0 25
This while(input.good()); is probably not what you intended...
Use this:
string line;
while(getline (input,line))
{
If the getline() works then the loop is entered.
If you try and read past the EOF then it will fail and the loop will exit.
So this should word as expected.
Rather than using strtok() (which damages the string) and atoi() which is non portable.
Use std::stringstream
std::stringstream linestream(line);
int lineNumber;
linestream >> lineNumber; // reads a number from the line.
Don't explicitly close() the stream (unless you want to detect and correct for any problems). The file will be closed when the object goes out of scope at the end of the function.
You want to use eof() not good().
Avoid strtok. There are other ways to tokenize a string that do not require the called function to modify your string. The fact that it modifies the string it tokenizes could also be what causes the loop here.
But more likely, the good() member is not the right one. Try !input.eof() or similar, depending on what you need.
While you've already gotten some answers to the question you asked, perhaps it's worth answering some you should have about the code that you didn't ask:
void loadInstructions(char* fileName)
Since the function isn't going to modify the file name, you almost certainly want to change this to:
void loadInstructions(char const *fileName)
or
void loadInstructions(std::string const &fileName)
ifstream input;
input.open(fileName);
It's much cleaner to combine these:
ifstream input(fileName);
or (if you passed a string instead):
ifstream input(fileName.c_str());
while(!input.eof());
This has already been covered.
string line;
getline (input,line);
char * lineChar = &line[0];
//instruction cmd; //This will be used later to store instructions from the parse
char * token;
token = strtok (lineChar," ");
// just trying to get the line number for now
int lineNumber = atoi(token);
Most of this is just extraneous. You can just let atoi convert directly from the original input:
string line;
getline(input, line);
int lineNumber = atoi(line);
If you're going to tokenize more later, you can use strtol instead:
char *end_ptr;
int lineNumber = strtol(line, &end_ptr, 10);
This will set end_ptr to point just past the end of the part that strtol converted.
I'd also consider an alternative though: moving your code for reading and parsing a line into a class, and define operator>> to read those:
struct line {
int number;
operator int() { return number; }
};
std::istream &operator>>(std::istream &is, line &l) {
// Just for fun, we'll read the data in an alternative fashion.
// Instead of read a line into a buffer, then parse out the first number,
// we'll read a number from the stream, then ignore the rest of the line.
// I usually prefer the other way, but this is worth knowing as well.
is >> l.number;
// When you're ready to parse more, add the extra parsing code here.
is.ignore(std::numeric_limits<std::istream::pos_type>::max, '\n');
return is;
}
With this in place, we can print out the line numbers pretty easily:
std::copy(std::istream_iterator<line>(input),
std::istream_iterator<line>(),
std::ostream_iterator<int>(std::cout, "\n"));
input.close();
I'd usually just let the stream close automatically when it goes out of scope.

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.)