How to read string with spaces, until a '\r'? - c++

I want to read a long string, and split it into smaller ones, where each new row of the big string is an entry for the smaller ones. However, I only want it to break at '\r', and not at spaces.
This is a sample code of what I'm doing right now:
std::vector<std::string> m_list;
std::ifstream input("data.txt");
std::string str;
std::getline(input, str);
std::istringstream iss(str);
std::string temp;
while (iss >> temp)
{
m_list.push_back(temp);
}
However, this code breaks the string upon encountering spaces, as well.

You where definitely on the right path, you actually just need to skip the direct string stream operation. This should work fine:
std::vector<std::string> m_list;
std::ifstream input("data.txt");
std::string str;
while (std::getline(input, str, '\r'))
{
m_list.push_back(str);
}

Related

How to split the elements of a text file in C++

i have a text file called builders.txt that contains some data
Reliable Rover:70:1.
Sloppy Simon:20:4.
Technical Tom:90:3.
Within my main file i have a function declaration related to this specific text file
void Builder() {
std:string name;
int ability;
int variability;
}
this is my read file function
std::vector<std::string> lines;
std::string inputFile1 = "Builders.txt";
std::string inputFile2 = "Parts.txt";
std::string inputFile3 = "Customers.txt";
std::string outputFile = "output.txt";
std::string input;
void readFile(std::string const& inputFile1, std::string const& inputFile2, std::string const& inputFile3,
std::vector<std::string>& lines) //function to read Builders, Customers and Parts text file
{
std::ifstream file1(inputFile1);
std::ifstream file2(inputFile2);
std::ifstream file3(inputFile3);
std::string line;
while(std::getline(file1, line))
{
lines.push_back(line);
}
while(std::getline(file2, line))
{
lines.push_back(line);
}
while(std::getline(file3, line))
{
lines.push_back(line);
}
}
This is my attempt
std::vector<std::string> lines;
std::string inputFile1 = "Builders.txt";
std::istringstream newStream(inputFile1);
std::string input;
void readFile(std::string const& newStream,std::vector<std::string>& lines)
{
std::ifstream file1(newStream);
std::string line;
while(std::getline(file1, line,":"))
{
lines.push_back(line);
}
When i run this code i recieve the error "no instance of overload function getline"
My question is given the text file how can i split the text file so that, for example, Reliable Rover is the name, 70 is the ability and 1 is the variability for the 1st record. Another example would be Sloppy Simon being the name, 20 being the ability and 4 being variaiblity. If the question is to vague or requires futher details please let me know
Thankyou
As #thomas-sablik mentioned, a simple solution is to read the file line by line and read each element from the line:
std::ifstream f("builder.txt");
std::string line;
// read each line
while (std::getline(f, line)) {
std::string token;
std::istringstream ss(line);
// then read each element by delimiter
while (std::getline(ss, token, ':'))
std::cout << token << std::endl;
}
don't forget to include sstream for using stringstreams.
Note: refer to cppreference, third parameter of std::getline is delim and is a character but you pass it as a string. So change:
while(std::getline(file1, line,":"))
to:
while(std::getline(file1, line,':'))
Here is a naive approach I came up with:
std::string name;
int ability;
int variability;
char read;
while (ifs >> read) { // read until the end of the file
// adding read into name
while (read != ':') {
name += read;
ifs >> read;
}
ifs >> ability;
ifs >> read; // Remove ':'
ifs >> variability;
ifs >> read; // Remove '.'
// Code to deal with the three variables
name = "";
}
Hope it helps.

Reading key value pairs from a file and ignoring # comment lines

I would like to read key value pairs from a file, while ignoring comment lines.
imagine a file like:
key1=value1
#ignore me!
I came up with this,
a) it seems very clunky and
b) it doesn't work if the '=' isn't surrounded by spaces. The lineStream isn't being properly split and the entire line is being read into "key".
std::ifstream infile(configFile);
std::string line;
std::map<std::string, std::string> properties;
while (getline(infile, line)) {
//todo: trim start of line
if (line.length() > 0 && line[0] != '#') {
std::istringstream lineStream(line);
std::string key, value;
char delim;
if ((lineStream >> key >> delim >> value) && (delim == '=')) {
properties[key] = value;
}
}
}
Also, comments on my code style are welcome :)
I had to make some interpreter that read in a configuration file and stored it's values recently, this is the way I did it, it ignores lines starting with # :
typedef std::map<std::string, std::string> ConfigInfo;
ConfigInfo configValues;
std::string line;
while (std::getline(fileStream, line))
{
std::istringstream is_line(line);
std::string key;
if (std::getline(is_line, key, '='))
{
std::string value;
if (key[0] == '#')
continue;
if (std::getline(is_line, value))
{
configValues[key] = value;
}
}
}
fileStream being being an fstream of a file.
Partly from https://stackoverflow.com/a/6892829/1870760
Doesn't look that bad. I just would use string::find to find the equal sign, instead of generating the lineStream. Then take the substring from index zero to the equal-sign position and trim it. (Unfortunately, you have to write the trim routine yourself or use the boost one.) Then take the substring behind the equal sign and also trim it.

Tokenize stringstream

I need cut string stream according custom separator. Current code cuts just acording to several standart separators. How to define and cut stringstream to string line according to custom delimiter?
std::istringstream input;
input.str("1\n2\n3\n4\n5\n6\n7\n");
int sum = 0;
for (std::string line; std::getline(input, line); )
{
cout<<line;
}
If you have one delimiter you want to use and it's a single character, you can just pass it to the 3-parameter overload of std::getline():
std::istringstream input;
input.str("1;2;3;4;5;6;7;");
int sum = 0;
for (std::string field; std::getline(input, field, ';'); )
{
std::cout<<field;
}
Live example
For other situations (multi-character delimiter, multiple delimiters), you might want to consider using Boost.Tokenizer.
Use third argument of overloaded std::getline
for (std::string line; std::getline(input, line, delimiter ); )
{
std::cout<< line <<'\n';
}

Reading in file with delimiter

How do I read in lines from a file and assign specific segments of that line to the information in structs? And how can I stop at a blank line, then continue again until end of file is reached?
Background: I am building a program that will take an input file, read in information, and use double hashing for that information to be put in the correct index of the hashtable.
Suppose I have the struct:
struct Data
{
string city;
string state;
string zipCode;
};
But the lines in the file are in the following format:
20
85086,Phoenix,Arizona
56065,Minneapolis,Minnesota
85281
56065
Sorry but I still cannot seem to figure this out. I am having a really hard time reading in the file. The first line is basically the size of the hash table to be constructed. The next blank line should be ignored. Then the next two lines are information that should go into the struct and be hashed into the hash table. Then another blank line should be ignored. And finally, the last two lines are input that need to be matched to see if they exist in the hash table or not. So in this case, 85281 is not found. While 56065 is found.
As the other two answers point out you have to use std::getline, but this is how I would do it:
if (std::getline(is, zipcode, ',') &&
std::getline(is, city, ',') &&
std::getline(is, state))
{
d.zipCode = std::stoi(zipcode);
}
The only real change I made is that I encased the extractions within an if statement so you can check if these reads succeeded. Moreover, in order for this to be done easily (you wouldn't want to type the above out for every Data object), you can put this inside a function.
You can overload the >> operator for the Data class like so:
std::istream& operator>>(std::istream& is, Data& d)
{
std::string zipcode;
if (std::getline(is, zipcode, ',') &&
std::getline(is, d.city, ',') &&
std::getline(is, d.state))
{
d.zipCode = std::stoi(zipcode);
}
return is;
}
Now it becomes as simple as doing:
Data d;
if (std::cin >> d)
{
std::cout << "Yes! It worked!";
}
You can use a getline function from <string> like this:
string str; // This will store your tokens
ifstream file("data.txt");
while (getline(file, str, ',') // You can have a different delimiter
{
// Process your data
}
You can also use stringstream:
stringstream ss(line); // Line is from your input data file
while (ss >> str) // str is to store your token
{
// Process your data here
}
It's just a hint. Hope it helps you.
All you need is function std::getline
For example
std::string s;
std::getline( YourFileStream, s, ',' );
To convert a string to int you can use function std::stoi
Or you can read a whole line and then use std::istringstream to extract each data with the same function std::getline. For example
Data d = {};
std::string line;
std::getline( YourFileStream, line );
std::istringstream is( line );
std::string zipCode;
std::getline( is, zipCode, ',' );
d.zipCode = std::stoi( zipCode );
std::getline( is, d.city, ',' );
std::getline( is, d.state, ',' );

Using isstringstream as a tokenizer in a loop

I would like some help understanding how to deal with isstringstream objects.
I am trying to tokenize each line of a file so I can re-write it in another format after checking certain data values in the tokens. I am loading each line in a tokenVector and iterating through the vector. My code works, but what concerns me is that I have to instantiate a isstringstrem object for each iteration otherwise it does not work. That does not feel right. Her is my code:
std::string line;//each file line
std::ifstream myFile (info.txt.c_str());
if(myFile.is_open()){
getline(myFile, line);
std::vector<std::string> tokenVector;
//create a isstringstream object for tokenizing each line of the file
std::istringstream hasTokens(line);
while(hasTokens)
{
std::string substring;
if(! getline(hasTokens, substring,','))
break;
tokenVector.push_back(substring);
}
//look for some known header names for validation
if(!tokenVector.empty()){
if(!(tokenVector[0]=="Time")&&(tokenVector[1] == "Group")&&(tokenVector[2]=="Perception")&&(tokenVector[3] == "Sign")){
setErrorMesssage("Invalid Header in myFile");
return false;
}
tokenVector.clear();
}
//clear the isstringstream object
hasTokens.str(std::string());
//if header validates, do rest of file
while(myFile.good()){
getline(myFile , line);
//break line into tokens using istringstream
std::istringstream hasTokens(line);
//reload the vector of tokens for each line
while(hasTokens)
{
std::string substring;
if(! getline(hasTokens, substring,','))
break;
tokenVector.push_back(substring);
}
otherFileWritingFunction(tokenVector[0], tokenVector[2], tokenVector[4]);
tokenVector.clear();
hasTokens.str(std::string());
}//end while
}//end if is_open
This code works, but its not correct because I should only have to instantiate isstringstream once (I think). If I try "hasTokens.str(line)" for each iteration using just the original instantiation of hasTokens, as some example have suggested, it does not work, so I would really appreciate a suggestion.
Thanks
Nope, your worries are misplaced. Create a new stream object when you need it, and dispose of it when you're done. That's the spirit of C++. An object for each purpose, and a purpose for each object (misquoting Frank Herbert). There's nothing "expensive" about constructing a string stream that wouldn't also happen when you reassign the string data of an existing string stream.
Your code is very noisy and redundant, though. The standard idiom goes like this:
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
std::string token;
while (iss >> token) { /* do stuff */ }
}
Compressed version (some would call this abuse):
for (std::string line; std::getline(infile, line); )
{
std::istringstream iss(line);
for (std::string token; iss >> token; ) { /* ... */ }
}
The second std::istringstream declaration has an entirely different scope and is being constructed in each iteration so hasTokens.str(std::string()); has no effect.
You could reuse the same object if you did hasTokens.str(line) in the while loop instead.