How do i scan line and extract integers and strings containing whitespaces - c++

I have a file with following lines:
51:HD L80 Phone:78
22:Nokia Phone:91
I need to split these into 3 separate variables
(int, string, int)
int id = line[0]
string phoneName = line[1]
int price = line [2]
I have tried many solutions for example:
std::ifstream filein("records");
for (std::string line; std::getline(filein, line); )
{
// std::cout << line << std::endl;
std::istringstream iss (line);
std::string word;
std::vector<string> tempString;
while(std::getline(iss,word,',')){
tempString.push_back(word);
// std::cout << word << "\n";
}
However in this example I do get the values but they are coming in a stream and not in one go. I do not want to save them into vector (no other way to store the incoming values) but call a function immediately after getting all the 3 values.
SOLUTION
This is a modification of the accepted answer:
`for (std::string line; std::getline(filein, line); )
{
// std::cout << line << std::endl;
std::istringstream iss (line);
for (int stockID; iss >> stockID; )
{
char eater;
iss >> eater; // this gets rid of the : after reading the first int
std::string stockName;
std::getline(iss, stockName, ':'); // reads to the next :, tosses it out and stores the rest in word
std::string catagory;
std::getline(iss, catagory, ':'); // reads to the next :, tosses it out and stores the rest in word
std::string subCatagory;
std::getline(iss, subCatagory, ':');
int stockPrice;
iss >> stockPrice;
iss >> eater; // this gets rid of the : after reading the first int
int stockQTY;
iss >> stockQTY; // get the last int
// iss >> eater;
// std::cout << stockName << "\n";
Record recordd = Record(stockID,stockName,catagory,subCatagory,stockPrice,stockQTY);
record.push_back(recordd);
}
}`
for when text file contains:
51:HD L80 Phone:Mobile:Samsung:480:40
22:Nokia Phone:Mobile:Nokia:380:200

There is no reason to use a std::stringstream here if you know you are going to have exactly 3 columns in every row. Instead you can read those values directly from the file, store them in temporaries, and then call the function with those temporary variables.
for (int a; filein >> a; )
{
char eater;
filein >> eater; // this gets rid of the : after reading the first int
std::string word;
std::getline(filein, word, ':'); // reads to the next :, tosses it out and stores the rest in word
int b;
filein >> b; // get the last int
function_to_call(a, word, b);
}

You can find different ways to split a string here:
https://www.fluentcpp.com/2017/04/21/how-to-split-a-string-in-c/
Example:
std::vector<std::string> split(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;
}

Related

How to use getline to delimit by comma *and* <space> in c++?

chicken, for sale, 60
microwave, wanted, 201.
These are examples lines from my txt file. Right now this is my code:
while(getline(data, word, '\n')){
ss<<word;
while(getline(ss, word, ',')){//prints entire file
cout<<word<<endl;
}
}
and my output is:
chicken
for sale
60
My file is succesfully parsed line by line, but I also need to get rid of that space after each comma. Adding a space after the comma here just gives me an error "no matching function to call 'getline(...:
while(getline(ss, word, ', '))
SOLUTION: I just used the erase function
if(word[0]==' '){//eliminates space
word.erase(0,1);
}
getline just parses single parameter.
If you want to parse the multiple delimiters, You can use boost library.
std::string delimiters("|,:-;");
std::vector<std::string> parts;
boost::split(parts, inputString, boost::is_any_of(delimiters));
for(int i = 0; i<parts.size();i++ ) {
std::cout <<parts[i] << " ";
}
You could use std::ws to get rid of any leading whitespace of each part:
while(getline(ss >> std::ws, word, ','))
Try something like this:
std::string line;
std::string tok;
while (std::getline(data, line))
{
std::istringstream iss(line);
while (std::getline(iss >> std::ws, tok, ',')) {
tok.erase(tok.find_last_not_of(" \t\r\n") + 1);
std::cout << tok << std::endl;
}
}
Live demo
You can then wrap the above logic in a custom overloaded >> operator:
class token : public std::string {};
std::istream& operator>>(std::istream &in, token &out)
{
out.clear();
if (std::getline(in >> std::ws, out, ','))
out.erase(out.find_last_not_of(" \t\r\n") + 1);
return in;
}
std::string line;
token tok;
while (std::getline(data, line))
{
std::istringstream iss(line);
while (iss >> tok) {
std::cout << tok << std::endl;
}
}
Live demo
SOLUTION: I just used the erase function
if(word[0]==' '){//eliminates space
word.erase(0,1);
}

C++ ifstream, issue loading with ";"

int a, b;
while (infile >> a >> b)
{
// process pair (a,b)
}
So this is the code i've been watching but i ran into a problem because my strings doesn't have whitespaces between them, they have ";"
My code:
void load(string filename){ // [LOAD]
string line;
ifstream myfile(filename);
string thename;
string thenumber;
if (myfile.is_open())
{
while (myfile >> thename >> thenumber)
{
cout << thename << thenumber << endl;
//map_name.insert(make_pair(thename,thenumber));
}
myfile.close();
}
else cout << "Unable to open file";
}
[Inside the txt.file]
123;peter
789;oskar
456;jon
What i get right now is "thename" as 123;peter and "thenumber" as 789;oskar.
I want "thename" as peter and "thenumber" as 123 so i can then insert it back into my map correctly, How?
The infile >> a read from infile the eligible type for a. In your case a is int so '>>' expect to find an int. In your code myfile >> thename >> thenumber both are string type so they expect string type in your file. The problem is that string include ';' so the variable name will take all the row until it find \n(new line).
in your code
std::string thename, thenumber;
char delimeter(';'); //It is always '-' is it?
std::getline(std::cin, thename, delimeter);
std::getline(std::cin, thenumber);
also thenumber will be string type. To convert your thenumber into int:
std::istringstream ss(thenumber);
int i;
ss >> i;
if (ss.fail())
{
// Error
}
else
{
std::cout << "The integer value is: " << i;
}
return 0;
It is fairly simple to read in a file in the format. You can use std::getline with a different delimiter to tell it where to stop reading the input.
while(getline(myfile, thenumber, ';')) // reads until ';' or end of file
{
getline(myfile, thename); // reads until newline or end of file
map_name.insert(make_pair(thename,thenumber));
}
You have to input a single string and then split it to get the name and number
....
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
....
void load(string filename){
..........
if (myfile.is_open())
{
while (myfile >>whole)
{
std::vector<std::string> parts = split(whole, ';');
name = parts[0];
number = parts[1];
}
}

istringsteam with line breaks

Okay I read that if we have a string s =" 1 2 3"
we can do :
istringstream iss(s);
int a;
int b;
int c;
iss >> a >> b >> c;
Lets say we have a text file with the following :
test1
100 ms
test2
200 ms
test3
300 ms
ifstream in ("test.txt")
string s;
while (getline(in, s))
{
// I want to store the integers only to a b and c, How ?
}
1) You can rely on succesful convertions to int:
int value;
std::string buffer;
while(std::getline(iss, buffer,' '))
{
if(std::istringstream(buffer) >> value)
{
std::cout << value << std::endl;
}
}
2) or just skip over unnecessary data:
int value;
std::string buffer;
while(iss >> buffer)
{
iss >> value >> buffer;
std::cout << value << std::endl;
}
If you know the pattern of the details in the text file, you could parse through all the details, but only store the int values. For example:
ifstream in ("test.txt")
string s;
while (getline(in, s))
{
getline(in,s); //read the line after 'test'.
string temp;
istringstream strm(s);
s >> temp;
int a = stoi(temp) // assuming you are using C++11. Else, atoi(temp.c_str())
s >> temp;
getline(in,s); // for the line with blank space
}
This above code is still somewhat of a inelegant hack. What you could do besides this is use random file operations in C++. They allow you to move your pointer for reading data from a file. Refer to this link for more information: http://www.learncpp.com/cpp-tutorial/137-random-file-io/
PS: I haven't run this code on my system, but I guess it should work. The second method works for sure as I have used it before.

How to save and restore an std::istringstream's buffer?

I am using a istringstream to read a string word by word. However, when my condition fails I need to be able to revert the istringstream to before the previous word was read. My example code works, but I want to know if there is a more direct way to use streams to accomplish this.
std::string str("my string");
std::istringstream iss(str);
std::ostringstream ossBackup << iss.rdbuf(); // Writes contents of buffer and in the process changes the buffer
std::string strBackup(ossBackup.str()); // Buffer has been saved as string
iss.str(strBackup); // Use string to restore iss's buffer
iss.clear(); // Clear error states
iss >> word; // Now that I have a backup read the 1st word ("my" was read)
// Revert the `istringstream` to before the previous word was read.
iss.str(strBackup); // Restore iss to before last word was read
iss.clear(); // Clear error states
iss >> word; // "my" was read again
You can use tellg() and seekg() to save and restore your position if you like:
#include <string>
#include <sstream>
int main()
{
std::istringstream iss("some text");
std::string word;
// save the position
std::streampos pos = iss.tellg();
// read a word
if(iss >> word)
std::cout << word << '\n';
iss.clear(); // clear eof or other errors
iss.seekg(pos); // move to saved position
while(iss >> word)
std::cout << word << '\n';
}
This is really only guaranteed to work for stringstream's, but you can repeatedly call unget() until you've reached a space character:
#include <iostream>
#include <sstream>
template <int n>
std::istream& back(std::istream& is)
{
bool state = is.good();
auto& f = std::use_facet<std::ctype<char>>(is.getloc());
for (int i = 0; i < n && is; ++i)
while (is.unget() && !f.is(f.space, is.peek()));
if (state && !is)
is.clear();
return is;
}
int main()
{
std::stringstream iss("hello world");
std::string str;
std::cout << "Value Before: ";
iss >> str;
std::cout << str << std::endl;
iss >> back<1>; // go back one word
std::cout << "Value after: ";
iss >> str;
std::cout << str;
}
Live Demo

Reading in a file with delimiter and blank lines for hashing program

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
I 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.
This is what I have and it doesn't seem to be doing what I want it to do:
int main(int argc, char *argv[])
{
string str;
//first line of file is size of hashtable
getline(cin, str);
stringstream ss(str);
int hashSize;
ss >> hashSize;
//construct hash table
Location *hashTable = new Location[hashSize];
//skip next line
getline(cin, str);
string blank = " ";
while(getline(cin, str))
{
{
//next lines are data
Location locate;
string line;
getline(cin, line);
istringstream is(line);
getline(is, locate.zipCode, ',');
getline(is, locate.city, ',');
getline(is, locate.state, ',');
insertElementIntoHash(hashTable, locate, hashSize);
}
}
dispHashTable(hashTable, hashSize);
//read third set of lines that check if the zipCodes are in the hashtable or not
while(getline(cin, str))
{
//stop reading at a blank line or in this case, end of file
stringstream is(str);
string searchZipCode;
is >> searchZipCode;
searchElementInHash(hashTable, hashSize, searchZipCode);
}
//delete hash table after use
delete []hashTable;
return 0;
}
You might read the input this way:
#include <iostream>
#include <sstream>
#include <vector>
struct Location
{
std::string city;
std::string state;
std::string zipCode;
};
int main(int argc, char *argv[]) {
std::istringstream input(
"2\n"
"\n"
"85086,Phoenix,Arizona\n"
"56065,Minneapolis,Minnesota\n"
"\n"
"85281\n"
"56065\n"
);
// Make the size unsigned, to avoid signed/unsigned compare warnings.
unsigned hashSize;
std::string line;
getline(input, line);
std::istringstream hash_line(line);
// Ignore white space.
if( ! (hash_line >> hashSize >> std::ws && hash_line.eof())) {
std::cerr << "Error: Invalid file format [1].\n" << line << '\n';
return -1;
}
else {
getline(input, line);
std::istringstream first_blank_line(line);
// Ignore white space.
first_blank_line >> std::ws;
if( ! first_blank_line.eof()) {
// Missing blank line.
std::cerr << "Error: Invalid file format [2].\n" << line << '\n';
return -2;
}
else {
// Have a local variable (No need to allocate it)
// (Is it a hash table !???)
std::vector<Location> hashTable;
hashTable.reserve(hashSize);
while(hashTable.size() < hashSize && getline(input, line)) {
std::istringstream data_line(line);
Location locate;
getline(data_line, locate.zipCode, ',');
getline(data_line, locate.city, ',');
getline(data_line, locate.state); // Note: No comma here.
if(data_line && data_line.eof()) {
// Note: The fields may have leading and/or trailing white space.
std::cout
<< "Insert the location into the hash table.\n"
<< locate.zipCode << '\n'
<< locate.city << '\n'
<< locate.state << '\n';
hashTable.push_back(locate);
}
else {
std::cerr << "Error: Invalid file format [3].\n" << line << '\n';
return -3;
}
}
if(hashTable.size() != hashSize) {
std::cerr << "Error: Invalid file format [4].\n";
return -4;
}
else {
getline(input, line);
std::istringstream second_blank_line(line);
// Ignore white space.
second_blank_line >> std::ws;
if( ! second_blank_line.eof()) {
// Missing blank line.
std::cerr << "Error: Invalid file format [5].\n";
return -5;
}
else {
std::string searchZipCode;
while(input >> searchZipCode) {
// Search element in the hash table
}
}
}
}
}
return 0;
}
Following modification should work:
//skip next line
getline(cin, str);
string blank = " ";
string line;
while(getline(cin, line) && (line != ""))
{
{
//next lines are data
Location locate;
istringstream is(line);
getline(is, locate.zipCode, ',');
getline(is, locate.city, ',');
getline(is, locate.state, ',');
insertElementIntoHash(hashTable, locate, hashSize);
}
}