I have an input text file. The first line has two int numbers a and b, and the second line is a string. I want to use formatted input to do file >> a >> b, and then unformatted input to get the characters of the string one by one. In between the two steps, I need to skip over the '\n' character at the end of the first line. I used
while(file.get()<=' ' && !file.eof()); // skip all unprintable chars
if(!file.eof()) file.unget(); // keep the eof sign once triggered
to make the input format more flexible. The user can now separate the numbers a and b from the string using an arbitrary number of empty lines '\n', tab keys '\t', and/or space keys ' ' -- the same freedom he has to separate the numbers a and b. There's even no problem reading in Linux a text file copied from Windows when every end of line now becomes "\r\n".
Is there an ifstream function that does the same thing (skip all chars <=' ' until the next printable char or EOF is reached)? The ignore function does not seem to do that.
Yes, there is: std::ws manipulator. It skips whitespace characters until a non-whitespace is found or end of stream is reached.. It is similar to use of whitespace character in scanf format string.
In fact, it is used by formatted input before actually starting to parse characters.
You can use it like that:
int x;
std::string str;
std::cin >> x >> std::ws;
std::getline(std::cin, str);
//...
//std::vector<int> vec;
for(auto& e: vec) {
std::cin >> e;
}
std::getline(std::cin >> std::ws, str);
Related
I am teaching an after school club about cin and getline() right now and I know you're suppose to put a cin.ignore() when switching from cin and getline; I also know why we do that, it all makes sense.
However.... A student put cin.ignore() AFTER the getline() call and it still worked as if they put it before getline(), does anyone know why?
What I teach and what I know works.
string answer, line;
cin >> answer;
cin.ignore();
getline(cin, line);
What shouldn't work but does even though cin.ignore is after getline()?
string answer, line;
cin >> answer;
getline(cin, line);
cin.ignore();
Please read carefully how operator>> works, and what ignore() does.
by default, operator>> discards leading whitespace characters before extracting data needed for a value. This can be disabled with std::noskipws.
operator>> for integer types extracts as many characters as possible to interpret them as an integer.
operator>> for a string extracts only non-whitespace characters. Reading stops just before the first whitespace character.
by default, .ignore() discards just a single character.
to trim a line after operator>>, ignore should be used as: .ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This leads to the following cases:
type
input
read effect
remaining input
int
11
11
int
12
12
int
1\n
1
\n
int
1 \n
1
\n
int
13das
13
das
std::string
foo
"foo"
Your first example will fail if line ends with extra spaces.
Your second example may appear to be working if input contains leading whitespace, or other characters which are skipped do not have impact on reading the result (for example: +12 when reading int).
I am trying to use standard input(cin) to read in inputs until a blank line is hit. I tried many times but still fail to achieve it. Can anyone help me out?
The following is the input format. Note: 1. // are comments 2. Comments can be randomly distributed after the second line in the input. So I also need to clear those comments. Not sure how to do it.3.The first line has to be a single letter. 4.The second line has to be an integer.
A 8 goodgreatamazingwonderfulfantasticterrific//These are some random commentsbrilliantgeniusstackoverflow
The following is what I have right now. I'm trying to use getline but the program just reads in the first two lines(the letter and the number). Then the programs ends. Not sure what is going wrong:
void read() {
vector<string> my_vec;
char my_letter;
cin >> my_letter;
int my_num
cin >> my_num;
string current_word;
while (getline(cin, current_word)) {
if (current_word.empty()) {
break;
}
if (current_word[0] != '/' ) {
my_vec.push_back(current_word);
}
}
}
The extraction cin >> my_num; does not extract the newline (which is whitespace, so the next getline call extracts an empty line.
Alternative ways to solve this:
Always use line-based string extraction and subordinate string streams.
Use std::cin >> my_num >> std::ws to gobble whitespace.
Use std::cin.ignore(1, '\n') to gobble the one newline.
Use a dummy std::getline(std::cin, current_word) call to gobble the one newline.
Sometimes I use std::stringstream to parse a text file, e.g.
8 9
100 1002 3.345
100 102 2.345
std::stringstream ss(file);
int unused1, unused2, first_useful_value;
ss >> unused1 >> unused2;
ss >> first_useful_value >> ...
now suppose that the first line, i.e.
8 9
are useless values to me and I just need to discard them. I might consider the entire line useless or I might consider some of those values useless.
My question is: is there any way to discard a value of a given type without having to declare useless variables on the stack (either wasteful and less readable)?
You can use std::stringstream::ignore with delimeter \n to skip the first line as follows:
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
LIVE DEMO
or use as delimiter space or what ever separates your values to discard one at a time:
ss.ignore(std::numeric_limits<std::streamsize>::max(), ' '); // delimiter is space
ss.ignore(std::numeric_limits<std::streamsize>::max(), ','); // delimeter is comma
LIVE DEMO
When disabling whitespace skipping with chars and strings the behavior is different. It seems the only way to extract an entire string (including whitespace characters) is to use chars and noskipws. But this is not possible with strings because it won't extract after the first space.
std::string test = "a b c";
char c;
std::istringstream iss(test);
iss.unsetf(std::ios_base::skipws);
while (iss >> c)
std::cout << c;
will output a b c but change c to string and it only outputs a.
The >> operator for a string extracts words, and stops at the
first white space it sees. If it doesn't skip initial white
space, then it stops immediately, and returns an empty string.
You don't say how you want the string to be delimited. To read
until the end of line, just use std::getline. To read until
the end of file, you can use something like:
std::istringstream collector;
collector << iss.rdbuf();
std::string results = collector.str();
It's not the most efficient, but if the file is small, it will
do.
For a small portion of my project, I'm supposed to extract data from a text file using cin which my program will know where to cin from based on command line arguments. My issue is how to extract the four pieces of data and ignore the commas. For example, the .txt file will look like the following
(1,2,3,.)
(2,1,3,#)
(3,1,0,.)
In which case I need to extract the 1, the 2, the 3, and the . for the first line. Then move to the second line. When a blank newline is reached than I can exit the getline() scenario through a while loop.
I know I need to use getline() and I was able to extract the data by using the .at() function of the string generated by getline(). I became confused however when a coordinate such as the 1, the 2, or the 3, could be double digits. When this happened, my previous algorithm didn't work so I feel I'm overthinking things and there should be a simpler way to parse this data.
Thanks!
You can just use the >> operator to a dummy 'char' variable to read in the separators. This assumes you don't care about the 4th token and that it's always a single character:
char ch;
while (ss >> ch)
{
int a,b,c;
ss >> a >> ch >> b >> ch >> c >> ch >> ch >> ch;
}
A simple approach is to use sscanf, pass the string you read from cin to it as the first argument
sscanf(s, "(%d,%d,%d,%c)", &a, &b, &c))
If you want to parse the string from scratch, just focus the pattern.
In this case, the pattern is
'(', number, ',', number, ',', number, ',', char, ')'
So you can locate the three commas, then simply extract three numbers from between them.
A more complicated method is regex.
But C++ doesn't have native support for that (the Boost library does)