I'm overloading the operator>> function. It should take a string in input, with some whitespaces needed, explode the string at whitespaces and do other operations not relevant for the topic.
I have this code:
std::istream& operator>>(std::istream &in, Foo &f) {
std::string str;
in >> str;
std::cout << "str = " << str << std::endl; // for testing
// ...
return in;
}
Assuming to put this string (a complex number) as input:
3 + 2i
the std::cout function prints only 3. I tried to put the flag std::noskipws, but the problem is still there.
Is there any way to solve this issue?
Use std::getline function to read complete input line:
std::getline(in, str);
Related
I want to parse a file with the following content:
2 300
abc12 130
bcd22 456
3 400
abfg12 230
bcpd22 46
abfrg2 13
Here, 2 is the number of lines, 300 is the weight.
Each line has a string and a number(price). Same with 3 and 400.
I need to store 130, 456 in an array.
Currently, I am reading the file and each line is processed as std::string. I need help to progress further.
Code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//void processString(string line);
void process2(string line);
int main(int argc, char ** argv) {
cout << "You have entered " << argc <<
" arguments:" << "\n";
for (int i = 1; i < argc; ++i)
cout << argv[i] << "\n";
//2, 4 are the file names
//Reading file - market price file
string line;
ifstream myfile(argv[2]);
if (myfile.is_open()) {
while (getline(myfile, line)) {
// cout << line << '\n';
}
myfile.close();
} else cout << "Unable to open market price file";
//Reading file - price list file
string line_;
ifstream myfile2(argv[4]);
int c = 1;
if (myfile2.is_open()) {
while (getline(myfile2, line_)) {
// processString(line_);
process2(line_);
}
myfile2.close();
} else cout << "Unable to open price lists file";
//processString(line_);
return 0;
}
void process2(string line) {
string word = "";
for (auto x: line) {
if (x == ' ') {
word += " ";
} else {
word = word + x;
}
}
cout << word << endl;
}
Is there a split function like in Java, so I can split and store everything as tokens?
You have 2 questions in your post:
How do I parse this file in cpp?
Is there a split function like in Java, so I can split and store everything as tokens?
I will answer both questions and show a demo example.
Let's start with splitting a string into tokens. There are several possibilities. We start with the easy ones.
Since the tokens in your string are delimited by a whitespace, we can take advantage of the functionality of the extractor operator (>>). This will read data from an input stream, up to a whitespace and then converts this read data into the specified variable. You know that this operation can be chained.
Then for the example string
const std::string line{ "Token1 Token2 Token3 Token4" };
you can simply put that into a std::istringstream and then extract the variables from the stream:
std::istringstream iss1(line);
iss1 >> subString1 >> subString2 >> subString3 >> subString4;
The disadvantage is that you need to write a lot of stuff and you have to know the number of elements in the string.
We can overcome this problem with using a vector as the taget data store and fill it with its range constructor. The vectors range constructor takes a begin and and end interator and copies the data into it.
As iterator we use the std::istream_iterator. This will, in simple terms, call the extractor operator (>>) until all data is consumed. Whatever number of data we will have.
This will then look like the below:
std::istringstream iss2(line);
std::vector token(std::istream_iterator<std::string>(iss2), {});
This may look complicated, but is not. We define a variable "token" of type std::vector. We use its range constructor.
And, we can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction", C++17 required).
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed default initializer with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
There is an additional solution. It is the most powerful solution and hence maybe a little bit to complicated in the beginning.
With that can avoid the usage of std::istringstream and directly convert the string into tokens using std::sregex_token_iterator. Very simple to use. And the result is a one liner for splitting the original string:
std::vector<std::string> token2(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
So, modern C++ has a build in functionality which is exactly designed for the purpose of tokenizing strings. It is called std::sregex_token_iterator. What is this thing?
As it name says, it is an iterator. It will iterate over a string (hence the 's' in its name) and return the split up tokens. The tokens will be matched again a regular expression. Or, natively, the delimiter will be matched and the rest will be seen as token and returned. This will be controlled via the last flag in its constructor.
Let's have a look at this constructor:
token2(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
The first parameter is, where it should start in the source string, the 2nd parameter is the end position, up to which the iterator should work. The last parameter is:
1, if you want to have a positive match for the regex
-1, will return everything that not matches the regex
And last but not least the regex itself. Please read in the net abot regex'es. There are tons of pages available.
Please see a demo for all 3 solutions here:
#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <sstream>
#include <iterator>
#include <algorithm>
/// Split string into tokens
int main() {
// White space separated tokens in a string
const std::string line{ "Token1 Token2 Token3 Token4" };
// Solution 1: Use extractor operator ----------------------------------
// Here, we will store the result
std::string subString1{}, subString2{}, subString3{}, subString4{};
// Put the line into an istringstream for easier extraction
std::istringstream iss1(line);
iss1 >> subString1 >> subString2 >> subString3 >> subString4;
// Show result
std::cout << "\nSolution 1: Use inserter operator\n- Data: -\n" << subString1 << "\n"
<< subString2 << "\n" << subString3 << "\n" << subString4 << "\n";
// Solution 2: Use istream_iterator ----------------------------------
std::istringstream iss2(line);
std::vector token(std::istream_iterator<std::string>(iss2), {});
// Show result
std::cout << "\nSolution 2: Use istream_iterator\n- Data: -\n";
std::copy(token.begin(), token.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
// Solution 3: Use std::sregex_token_iterator ----------------------------------
const std::regex re(" ");
std::vector<std::string> token2(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
// Show result
std::cout << "\nSolution 3: Use sregex_token_iterator\n- Data: -\n";
std::copy(token2.begin(), token2.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
So, now the answer on how you could read you text file.
It is essential to create the correct data structures. Then, overwrite the inserter and extractor operator and put the above functionality in it.
Please see the below demo example. Of course there are many other possible solutions:
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
struct ItemAndPrice {
// Data
std::string item{};
unsigned int price{};
// Extractor
friend std::istream& operator >> (std::istream& is, ItemAndPrice& iap) {
// Read a complete line from the stream and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Read the item and price from that line and check, if that worked
if (std::istringstream iss(line); !(iss >> iap.item >> iap.price))
// There was an error, while reading item and price. Set failbit of input stream
is.setf(std::ios::failbit);
}
return is;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const ItemAndPrice& iap) {
// Simple output of our internal data
return os << iap.item << " " << iap.price;
}
};
struct MarketPrice {
// Data
std::vector<ItemAndPrice> marketPriceData{};
size_t numberOfElements() const { return marketPriceData.size(); }
unsigned int weight{};
// Extractor
friend std::istream& operator >> (std::istream& is, MarketPrice& mp) {
// Read a complete line from the stream and check, if that worked
if (std::string line{}; std::getline(is, line)) {
size_t numberOfEntries{};
// Read the number of following entries and the weigth from that line and check, if that worked
if (std::istringstream iss(line); (iss >> numberOfEntries >> mp.weight)) {
mp.marketPriceData.clear();
// Now copy the numberOfEntries next lines into our vector
std::copy_n(std::istream_iterator<ItemAndPrice>(is), numberOfEntries, std::back_inserter(mp.marketPriceData));
}
else {
// There was an error, while reading number of following entries and the weigth. Set failbit of input stream
is.setf(std::ios::failbit);
}
}
return is;
};
// Inserter
friend std::ostream& operator << (std::ostream& os, const MarketPrice& mp) {
// Simple output of our internal data
os << "\nNumber of Elements: " << mp.numberOfElements() << " Weight: " << mp.weight << "\n";
// Now copy all marekt price data to output stream
if (os) std::copy(mp.marketPriceData.begin(), mp.marketPriceData.end(), std::ostream_iterator<ItemAndPrice>(os, "\n"));
return os;
}
};
// For this example I do not use argv and argc and file streams.
// This, because on Stackoverflow, I do not have files on Stackoverflow
// So, I put the file data in an istringstream. But for the below example,
// there is no difference between a file stream or a string stream
std::istringstream sourceFile{R"(2 300
abc12 130
bcd22 456
3 400
abfg12 230
bcpd22 46
abfrg2 13)"};
int main() {
// Here we will store all the resulting data
// So, read the complete source file, parse the data and store result in vector
std::vector mp(std::istream_iterator<MarketPrice>(sourceFile), {});
// Now, all data are in mp. You may work with that now
// Show result on display
std::copy(mp.begin(), mp.end(), std::ostream_iterator<MarketPrice>(std::cout, "\n"));
return 0;
}
this is my function
void global::readData(lifeform &p)
{
std::ifstream ifs("data.txt");
int i=0,i_str=0;
std::string str,str2;
std::getline(ifs,str);
std::stringstream ss(str);
if(i<31) {
std::getline(ss,str2,',');
std::istringstream toint(str2);
toint >> i_str;
if(i_str ==1) { //here i_str can get 1
p.newgame=1;
while(i<31) {
std::getline(ss,str2,',');
toint >> i_str;
if(i==0) {
p.setName(str2); //this also works
}
if(i==1) {
p.setMaxHp(i_str); //doesnt work
}
if(i==2) {
p.setQName("main",str2); //works
}
i++;
//....
}
}
this is data.txt
1,furkan,100,No mission,
i tried to get what is written on the data.txt to str.there is no problem so far.after than i wanted to convert string to int it worked in the first converting but not the rest
i changed 1 to 0 to see if it works or not and it works but the rest i_str doesnt work
1) std::istringstream get his own copy of str2. It don't watch changes of this variable.
2) And how you should do it:
char c;
ss>>i_str//read int value from stream
>>c;//and throw away separator
You don't need getline here.
Warning: this answer is kind of overkill for the specific job at hand. It uses a couple of fairly general-purpose utilities that make the job easy, but probably would be worth writing if this was the only time/place you had a use for them (but it's not--they turn out to be quite useful in general, once you have them). I keep them in an "input_utils.h", where I can use them from almost anything quite easily.
The first is an input operator for a constant string. That may sound utterly pointless (if it's constant, you obviously can't read into it). What it does is read some fixed input from a stream, and verify that what was in the stream matches what you expected. The code looks like this:
std::istream &operator>>(std::istream &is, char const *label) {
if (is.flags() & std::ios::skipws)
while (std::isspace((unsigned char)is.peek()))
is.ignore(1);
while (is.peek() == *label) {
is.ignore(1);
++label;
}
if (*label)
is.setstate(std::ios::failbit);
return is;
}
This lets us read (most of) the commas in your input quite easily--verify that they're present, but otherwise ignore them. So, to read three numbers separated by commas, we would do something like this:
input_stream >> number1 >> "," >> number2 >> "," >> number3;
At least to me, the meaning seems pretty simple, straightforward and obvious (and the applicability to the job at hand seems almost equally obvious).
The second has a clear and obvious intent, but the implementation is a little less trivial than I'd like (but I don't know a way to make it any simpler and still do the job). Its code looks like this:
class line_proxy {
std::string &s;
char delim;
public:
line_proxy(std::string &s, char delim) : s(s), delim(delim) {}
friend std::istream &operator>>(std::istream &is, line_proxy const &p) {
std::getline(is, p.s, p.delim);
return is;
}
};
line_proxy line(std::string &in, char delim = '\n') {
return line_proxy(in, delim);
}
We use it like this:
std::string s;
input_stream >> line(s); // equivalent to `std::getline(input_stream, s);`
Again, even though the implementation is a bit on the complex side, the use seems pretty obvious (at least to me).
Once we have these, your job borders on trivial. The one part that I'd do that might initially seem a little on the tricky side would be implementing the reading in a stream extraction operator for the lifeform class:
#include "input_utils.h"
struct lifeform {
int index;
std::string name;
int level;
std::string mission;
friend std::istream &operator>>(std::istream &is, lifeform &f) {
return is >> f.index
>> ","
>> line(f.name, ',')
>> f.level
>> ","
>> line(f.mission, ',');
}
We might want to be able to write out a lifeform as well:
friend std::ostream &operator<<(std::ostream &os, lifeform const&f) {
return os << "Index: " << f.index
<< ", name: " << f.name
<< ", level: " << f.level
<< ", mission: " << f.mission;
}
...and a little code to test them and see that they work correctly:
int main() {
std::istringstream input("1,furkan,100,No mission,");
lifeform f;
input >> f;
std::cout << f;
}
I have the following struct:
struct Person{
std::string name;
std::string address;
std::string& personName(){ return name; }
std::string& personAddress(){return address;}
};
The exercise is to write a read function that will read name and address. For example, the function I first wrote was this:
std::istream &read(std::istream &is, Person &person){
is >> person.name >> person.address;
return is;
}
However this function fails to take more than a word for address. For example if input is:
Lee Goswell Road
The output will be person.name = "Lee" and person.address = "Goswell". What I want is the function to read the entire address basically. I did try solving this problem as follows, but I am not sure it is right because address is changed implicitly:
std::istream &read(std::istream &is, Person &person){
is >> person.name;
std::getline(std::cin, person.address);
return is;
}
Another thing to consider before saying I should write separate functions, the task is to write one function to take read both the name and address.
You can use operator>> in tandem with std::getline but you'll probably want to eat the white-space from the stream first.
Also rather than read, you should just create your own operator>>:
std::istream& operator>>(std::istream& is, Person& person){
is >> person.name >> std::ws;
std::getline(is, person.address);
return is;
}
You can then use this as follows:
std::istringstream foo("Lee Goswell Road\nJon Lois Lane");
Person bar;
foo >> bar;
std::cout << bar.name << std::endl << bar.address << std::endl;
Just read a word, skip leading whitespace, then read to a delimiter:
if (is >> person.name >> std::ws
std::getline(is, person.address)) {
// do something with the input
}
else {
// deal with input failure
}
std::ws simply skips leading whitespace and std::getline() reads to delimiter with '\n' being the default.
I am basically reading a .txt file and storing values.
For example:
Student- Mark
Tennis
It will store Mark into memory as the studentName.
Now...If it is just
Student-
Tennis
Then it will work fine and produce an error.
However, if the file looks like this
Student-(space)(nothing here)
Tennis
It will store Tennis into memory as the studentName, when if fact it should store nothing and produce an error. I use '\n' character to determine if there is anything after the - character. This is my code...
istream& operator>> (istream& is, Student& student)
{
is.get(buffer,200,'-');
is.get(ch);
if(is.peek() == '\n')
{
cout << "Nothing there" << endl;
}
is >> ws;
is.getline(student.studentName, 75);
}
I think it is because the is.peek() is recognizing white space, but then if I try removing white space using is >> ws, it removes the '\n' character and still stores Tennis as the studentName.
Would really mean a lot if someone could help me solve this problem.
If you want to ignore whitespace but not '\n' you can't use std::ws as easily: it will skip over all whitespace and aside from ' ' the characters '\n' (newline), '\t' (tab), and '\r' (carriage return) are considered whitespace (I think there are actually even a few more). You could redefine what whitespace means for your stream (by replacing the stream's std::locale with a custom std::ctype<char> facet which has changed idea of what whitespace means) but that's probably a bit more advanced (as far as I can tell, there is about a handful of people who could do that right away; ask about it and I'll answer that question if I notice it, though...). An easier approach is to simply read the tail of the line using std::getline() and see what's in there.
Another alternative is create your own manipulator, let's say, skipspace, and use that prior to checking for newline:
std::istream& skipspace(std::istream& in) {
std::istreambuf_iterator<char> it(in), end;
std::find_if(it, end, [](char c){ return c != ' '; });
return in;
}
// ...
if ((in >> skipspace).peek() != '\n') {
// ....
}
You don't need to peek characters. I would use std::getline() and let it handle line breaks for you, then use std::istringstream for parsing:
std::istream& operator>> (std::istream& is, Student& student)
{
std::string line;
if (!std::getline(is, line))
{
std::cout << "Can't read student name" << std::endl;
return is;
}
std::istringstream iss(line);
std::string ignore;
std::getline(iss, ignore, '-');
iss >> std::ws;
iss.getline(student.studentName, 75);
/*
read and store className if needed ...
if (!std::getline(is, line))
{
std::cout << "Can't read class name" << std::endl;
return is;
}
std::istringstream iss2(line);
iss2.getline(student.className, ...);
*/
return is;
}
Or, if you can change Student::studentName into a std::string instead of a char[]:
std::istream& operator>> (std::istream& is, Student& student)
{
std::string line;
if (!std::getline(is, line))
{
std::cout << "Can't read student name" << std::endl;
return is;
}
std::istringstream iss(line);
std::string ignore;
std::getline(iss, ignore, '-');
iss >> std::ws >> student.studentName;
/*
read and store className if needed ...
if (!std::getline(is, student.className))
{
std::cout << "Can't read class name" << std::endl;
return is;
}
*/
return is;
}
I've got an object (of class myObj) that contains multiple strings (a string pointer). I want to overload the >>operator so that I can read in multiple strings at a time.
This overloaded operator functions accept statements like:
cin >> str;
cout << str;
The only problem is that when I fill in a series of strings, it seems that only the first string gets correctly processed in the stream.
To insert:
istream &operator>>(istream &is, myObj &obj)
{
std::string line;
while (true)
{
std::getline(is, line);
if (not is)
break;
obj.add(line);
is >> line;
}
return is;
}
To extract
ostream &operator<<(ostream &os, myObj const &obj)
{
for(size_t idx = 0; idx != obj.size(); ++idx)
os << obj[idx];
return os;
}
The code compiles fine but when I cout the object only the first string is printed and the other strings are omitted.
So when I provide cin with:
Hi
Stack
Exchange
only Hi will be displayed.
Does anyone know why this happens?
Thanks in advance!
P.S I am new to Stack Exchange and I am working hard to formulate the problems as best as I can :)
Your loop will work like that:
std::getline(is, line);
Extracts and stores "Hi" in line, extracts the newline
obj.add(line);
Adds "Hi" to obj
is >> line;
Extracts and stores "Stack" in line, does not extracts the following newline
std::getline(is, line);
Extracts and stores an empty string in line, because next read char is a newline
obj.add(line);
Adds empty string "" to obj
is >> line;
Extracts and stores "Exchange" in line
std::getline(is, line);
Extracts nothing (end of input stream)
if (not is)
break;
Then the stream is at end, your loop exits.
Conclusion : you stored only Hi and an empty string
is >> line puts a leading newline in the stream which is being read by std::getline() during the following loop. Because std::getline() stops input when it finds a newline, this reads an empty string into line and thus the stream is put into an error state which the loop responds to by breaking out of it.
There doesn't seem to be a need for that last read. You can remove it.
Also, this is a more idiomatic way to loop with input. This way you don't have to check the state of the stream within the loop body:
for (std::string line; std::getline(is, line); )
{
obj.add(line);
}
And since there's only one token per line, you can use a formmatted extractor:
for (std::string word; is >> word; )
{
obj.add(word);
}