How to use getline to delimit by comma *and* <space> in c++? - 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);
}

Related

How do i scan line and extract integers and strings containing whitespaces

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;
}

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];
}
}

C++ Read file line by line then split each line using the delimiter

i have searched and got a good grasp of the solution from a previous post but I am still stuck a bit.
My problem is instead of "randomstring TAB number TAB number NL"
My data is "number (space colon space) number (space colon space) sentence"
I've edited the code below but still can't get it to work 100% because the parameters getline takes is (stream, string, delimiter).
For some reason, it only gets the first word of the sentence as well and not the rest.
Previous post
I want to read a txt file line by line and after reading each line, I want to split the line according to the tab "\t" and add each part to an element in a struct.
my struct is 1*char and 2*int
struct myStruct
{
char chr;
int v1;
int v2;
}
where chr can contain more than one character.
A line should be something like:
randomstring TAB number TAB number NL
SOLUTION
std::ifstream file("plop");
std::string line;
while(std::getline(file, line))
{
std::stringstream linestream(line);
std::string data;
int val1;
int val2;
// If you have truly tab delimited data use getline() with third parameter.
// If your data is just white space separated data
// then the operator >> will do (it reads a space separated word into a string).
std::getline(linestream, data, '\t'); // read up-to the first tab (discard tab).
// Read the integers using the operator >>
linestream >> val1 >> val2;
}
At the following code line, the data variable will hold the complete line. And linestream will be consumed, so further readings will not yield anything.
std::getline(linestream, data, '\t'); // read up-to the first tab (discard tab).
Instead, you can just work on the line like this
while (std::getline(file, line))
{
int token1 = std::stoi(line.substr(0, line.find(" : ")));
line.erase(0, line.find(" : ") + 3);
int token2 = std::stoi(line.substr(0, line.find(" : ")));
line.erase(0, line.find(" : ") + 3);
std::string token3 = line;
}
What exactly is your problem ?
//Title of this code
//clang 3.4
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <vector>
struct Data
{
int n1;
int n2;
std::string sequence;
};
std::ostream& operator<<(std::ostream& ostr, const Data& data)
{
ostr << "{" << data.n1 << "," << data.n2 << ",\"" << data.sequence << "\"}";
return ostr;
}
std::string& ltrim(std::string& s, const char* t = " \t")
{
s.erase(0, s.find_first_not_of(t));
return s;
}
std::string& rtrim(std::string& s, const char* t = " \t")
{
s.erase(s.find_last_not_of(t) + 1);
return s;
}
std::string& trim(std::string& s, const char* t = " \t")
{
return ltrim(rtrim(s, t), t);
}
int main()
{
std::string file_content{
"1\t1\t\n"
"2\t2\tsecond sequence\t\n"
"3\t3\tthird sequence\n"};
std::istringstream file_stream{file_content};
std::string line;
std::vector<Data> content;
while(std::getline(file_stream, line))
{
std::istringstream line_stream{line};
Data data{};
if(!(line_stream >> data.n1 >> data.n2))
{
std::cout << "Failed to parse line (numbers):\n" << line << "\n";
break;
}
auto numbers_end = line_stream.tellg();
if(numbers_end == -1)
{
std::cout << "Failed to parse line (sequence):\n" << line << "\n";
break;
}
data.sequence = line.substr(numbers_end);
trim(data.sequence);
content.push_back(std::move(data));
}
std::copy(content.cbegin(), content.cend(),
std::ostream_iterator<Data>(std::cout, "\n"));
}
Live
Live with colons

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);
}
}

C++ Reading file Tokens

another request sorry..
Right now I am reading the tokens in one by one and it works, but I want to know when there is a new line..
if my file contains
Hey Bob
Now
should give me
Hey
Bob
[NEW LINE]
NOW
Is there a way to do this without using getline?
Yes the operator>> when used with string read 'white space' separated words. A 'White space' includes space tab and new line characters.
If you want to read a line at a time use std::getline()
The line can then be tokenized separately with a string stream.
std::string line;
while(std::getline(std::cin,line))
{
// If you then want to tokenize the line use a string stream:
std::stringstream lineStream(line);
std::string token;
while(lineStream >> token)
{
std::cout << "Token(" << token << ")\n";
}
std::cout << "New Line Detected\n";
}
Small addition:
Without using getline()
So you really want to be able to detect a newline. This means that newline becomes another type of token. So lets assume that you have words separated by 'white spaces' as tokens and newline as its own token.
Then you can create a Token type.
Then all you have to do is write the stream operators for a token:
#include <iostream>
#include <fstream>
class Token
{
private:
friend std::ostream& operator<<(std::ostream&,Token const&);
friend std::istream& operator>>(std::istream&,Token&);
std::string value;
};
std::istream& operator>>(std::istream& str,Token& data)
{
// Check to make sure the stream is OK.
if (!str)
{ return str;
}
char x;
// Drop leading space
do
{
x = str.get();
}
while(str && isspace(x) && (x != '\n'));
// If the stream is done. exit now.
if (!str)
{
return str;
}
// We have skipped all white space up to the
// start of the first token. We can now modify data.
data.value ="";
// If the token is a '\n' We are finished.
if (x == '\n')
{ data.value = "\n";
return str;
}
// Otherwise read the next token in.
str.unget();
str >> data.value;
return str;
}
std::ostream& operator<<(std::ostream& str,Token const& data)
{
return str << data.value;
}
int main()
{
std::ifstream f("PLOP");
Token x;
while(f >> x)
{
std::cout << "Token(" << x << ")\n";
}
}
I don't know why you think std::getline is bad. You can still recognize newlines.
std::string token;
std::ifstream file("file.txt");
while(std::getline(file, token)) {
std::istringstream line(token);
while(line >> token) {
std::cout << "Token :" << token << std::endl;
}
if(file.unget().get() == '\n') {
std::cout << "newline found" << std::endl;
}
}
This is another cool and much less verbose way I came across to tokenize strings.
vector<string> vec; //we'll put all of the tokens in here
string token;
istringstream iss("put text here");
while ( getline(iss, token, '\n') ) {
vec.push_back(token);
}