I have a file full of lines in this format:
1 - 2: 3
I want to only load numbers using C++ streams. Whats the most elegant way to do it? I only thought about cin.get() and checikng each char if it is number or not.
I think this one would be the fastest -yet elegant- way:
int a, b, c;
scanf("%d-%d:%d", &a, &b, &c);
You can use a locale to change what things are read from the file as it is being read. That is, you will filter out all non-numeric values:
struct numeric_only: std::ctype<char>
{
numeric_only(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table()
{
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::space);
std::fill(&rc['0'], &rc[':'], std::ctype_base::digit);
return &rc[0];
}
};
std::fstream myFile("foo.txt");
myfile.imbue(std::locale(std::locale(), new numeric_only()));
Then when you read your file, it'll convert all non digits to spaces while leaving you only the numbers. After that, you can simply use your normal conversions to transform what is being read into ints.
std::vector<int> intFromFile;
std::istream_iterator<int> myFileIter(myFile);
std::istream_iterator<int> eos;
std::copy(myFileIter, eos, std::back_inserter(intFromFile));
Response to the comments below:
Here is what I did to get it to work
int main(int args, char** argv){
std::fstream blah;
blah.open("foo.txt", std::fstream::in);
if(!blah.is_open()){
std::cout << "no file";
return 0;
}
blah.imbue(std::locale(std::locale(), new numeric_only()));
std::vector<int> intFromFile;
std::istream_iterator<int> myFileIter(blah);
std::istream_iterator<int> eos;
std::copy(myFileIter, eos, std::back_inserter(intFromFile));
return 0;
}
And this put only the ints into the vector, nothing more, nothing less. The reason it wasn't working before was two fold:
I was filling up to '9' but not '9' itself. I've changed the fill to ':'
Numbers larger than what an int can hold are a problem. I'd suggest using longs.
I would recommend doing at least cursory sanity checks when reading this:
int a, b, c;
char dash, colon;
if (not (cin >> a >> dash >> b >> colon >> c) or dash != '-' or colon != ':')
Failure. Do something.
Sorry Konrad, but I recommend: never on pain of death, never never never (is that clear enough? :-) read formatted data from a file. Just don't.
There is only one correct way to do input of formatted data: read chunks of characters (typically lines but you can also read fixed length blocks).
Then parse the input text. You're not going to do a cursory check, you going to use a parser that guarantees to catch any formatting error, and report that error in a comprehensible way, take appropriate action (termination, skip the line and continue, whatever).
Separate input (the I/O operation) from parsing.
This advice from decades of experience as a commerical programmer: reading formatted input is for micky mouse proof-of-principal programs. Even if you have exclusive control of the creation of the file, always parse and check and report errors: after all, stuff changes, it may work today but not tomorrow.
I've been writing C++ for decades and I've never read an integer.
Simply,
ifstream file("file.txt");
int n1, n2, n3;
char tmp;
while (file.good()) {
file >> n1 >> tmp >> n2 >> tmp >> n3;
}
int a,b,c;
cin >> a;
cin.ignore(100,'-');
cin >> b;
cin.ignore(100,':');
cin >> c;
cout << "a = "<< a <<endl;
cout << "b = "<< b <<endl;
cout << "c = "<< c <<endl;
Input:
1 - 2: 3
Output:
a = 1
b = 2
c = 3
See yourself here : http://www.ideone.com/DT9KJ
Note: this can handle extra spaces also. So you can read even this:
1 - 2 : 3
Similar topic:
Using ifstream as fscanf
Related
I thought I understood handling bad input with cin.clear() and cin.ignore(), like it is explained here, but in the following example
#include <iostream>
#include <limits>
using namespace std; //I know that this isn't good practice.
int main () {
int a, b;
while (cout << "Input some int: " && !(cin >> a)) {
cout << "Wrong datatype!\n";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
while (cout << "Input some int: " && !(cin >> b)) {
cout << "Wrong datatype!\n";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
if (a > 1) cout << "Some event.\n";
if (b > 1) cout << "Some other event.\n";
return 0;
}
the behavior I want is only present when the unwanted input is some character.
So if I enter x and y, I will again be asked for two ints and get the appropriate outputs, same if I enter a char and an int two times.
However: If I input, say, 2.3, I will get
Input some int: Wrong datatype!
but won't have a chance to correct my input, since the result invariantly outputs "Some event." The second prompt just accepts the float right away.
What's happening, actually, is the 2 in 2.3 is being accepted by the first prompt, leaving .3 in the input buffer. The Wrong datatype! you are seeing is from your second prompt, seeing a ., which is not a valid character for an integer. You then, I assume, enter an integer which is accepted by your second prompt.
This fundamental approach is fragile, and error-prone.
Your obvious intent is to accept a line of input, and process it. If so, then the correct function to do that is std::getline(). That's what its purpose is. That's exactly what it does. The >> operator does not do that. That's not what it's for. Of course, by using the various auxiliary methods, like ignore(), and clear(), one can still achieve that goal, but, as you've discovered, using those functions correctly is not intuitive. Of course, you can spend copious time pouring over their documentation to understand their every semantic behavior, but why bother, when you can simply use std::getline(), and then move on to something else. It's simply easier to do that.
Of course, once a line of input is received, you would like to parse it into an integer. Now is the correct time to use >> to parse it:
std::string line;
if (std::getline(line, std::cin))
{
std::istringstream i{line};
int n;
if (i >> n)
{
// Input parsed
}
}
Isn't this simpler, more straightforward, and less of a gotcha?. Of course, entering "2.3" here will result in the >> operator parsing the "2", and succeeding, leaving ".3" unparsed. If you would like to detect this situation, simply use get() to see what's left in the std::istringstream. Perhaps accept any trailing whitespace, if you wish.
The problem here is when you enter something like 2.3 to a int cin is okay with that. It reads the 2, sees the . so it stops reading and stores the 2 in the variable and leaves the .3 in the buffer for the next call. So, you pass the first loop, get to the second loop, and then you fail as it tries to read in the . into b. Then you clear the .3 and you can enter another input. If you enter another 2.3 the same thing will happen and b will get 2 and the program continues on.
The "bullet proof" way to read in input is to read it in as a std::string and then parse that to make sure the full input was good. That would look like
std::string line;
while (cout << "Input some int: " && std::getline(cin, line)) {
std::stringstream ss(line);
ss >> a;
if (ss.eof()) // we did consume all the input
break;
else
cout << "Wrong datatype!\n";
}
while (cout << "Input some int: " && std::getline(cin, line)) {
std::stringstream ss(line);
ss >> b;
if (ss.eof()) // we did consume all the input
break;
else
cout << "Wrong datatype!\n";
}
When you input "2.3", cin will stop at '.', and interpret '2' as the desired input.
Then, you will clear cin, when the '.' is encountered, discarding 3.
If you then input a new integer, it will accept it.
Many answers here suggest the use of std::getline and string parsing, either using the string functions or stringstreams. This is quite inefficient and not the way the streams are supposed to be used.
Instead, parse the data when it is still in the input stream:
#include <iostream>
#include <cctype>
#include <limits>
struct read_int {
int& a;
read_int(int& aa) : a{ aa } { }
friend std::istream& operator >>(std::istream& is, read_int& ri) {
char delim;
while(!(is >> ri.a) || (delim = is.get(), delim != '\n' && !std::isspace(delim))) {
std::cerr << "Bad!\n";
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
return is;
}
};
int main() {
int a, b;
std::cin >> read_int(a) >> read_int(b);
std::cout << a << ' ' << b;
return 0;
}
This function will accept input like "4 5" or "4\n6" alike, but requests a new input for data like "4.2", discarding everything read before.
I am 90% done with a homework project of mine but this last step is kicking my butt.
I have a text file that I'm going to be reading from for my program with commands on each line.
Most of the commands are a single letter, but one of them is a letter with an integer behind it.
I ideally need to read the line, if it's just a char go right into a function I've already written for the "Command". If it has a specific character, "F" in this case, I need it to also read the integer that will be separated by a space and pass that into my other function for that command.
Example;
.txt file;
R
L
L
F 20
R
R
For those who are curious I'm mimicking the function of the Logo language that used the little "turtle" to make logo animations for my homework.
Edit
I did try researching some methods to do this but most that I came up with either grabbed just the one char, or involved strings with which I could pull each "line" but then have to read and convert what was in string to separate char and int. If that is truly the "best" way to do it I'll suck it up and do it but I wanted to see if there was something that wasn't initially obvious to me.
This would be my approach:
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
int main() {
ifstream readFromFile("test.txt");
vector<string> fileWords;
string word;
while (readFromFile >> word) {
try {
int number = stoi(word); // here is your number
cout << number << endl;
} catch (const invalid_argument& exception) {
cout << exception.what() << endl; // just for debug
}
fileWords.emplace_back(word);
}
for (const auto& word: fileWords) {
cout << word << ' ';
}
readFromFile.close();
}
It reads word by word, saves it on an array and it also checks if a word is an integer (using the std::stoi function).
Solution by OP.
Resolved Kinda.
I ended up changing my fstream input to;
integer = 0;
char ch;
while(infile >> ch)
if (ch == "F")
{
infile >> integer;
}
// do stuff with code, I used a switch
Then after the switch I put I put integer back to 0.
This pulls the data I needed and stored it in the correct variables.
This question already has answers here:
How to test whether stringstream operator>> has parsed a bad type and skip it
(5 answers)
Closed 8 years ago.
i have a stupid question.
I have a .txt file. Once opened, i need to take only numbers and skipping words.
Is there any method to check if next element is a word or not?
Because my file is like: word 1 2 word 1 2 3 4 5 6...
int n,e;
string s;
ifstream myfile("input.txt");
and so i think that's a stupid method to avoid the problem using a string and put the content in a string and then taking numbers, right like this:
myfile >> s;
myfile >> n;
myfile >> e;
You can do the following
int num = 0;
while(myfile >> num || !myfile.eof()) {
if(myfile.fail()) { // Number input failed, skip the word
myfile.clear();
string dummy;
myfile >> dummy;
continue;
}
cout << num << endl; // Do whatever necessary with the next number read
}
See a complete, working sample here
When reading in from a file as you are doing, all data is seen as a string. You must check to see if the string is a number. Here is a way to convert a string to an integer (IF THAT STRING IS AN INTEGER): atoi() function
Be careful though, you must pass it a c-string.
You can get the all data as a string and try convert the data to an integer in a try {} catch () { } block. If the data is real an integer, perform the operation in try section, else if code go to the catch and don't do any operation in catch.
Oops it's already solved. But worth to mention, there is also possibility to:
either read individual chars from the stream and pushback() if they are digits before using operator >>
or peek() the next chars in the stream without reading it to decisde whether to ignore it or to use operator >>
Just be carefull about the '-' which is not a digit but could be the sign of an interger.
Here a small example :
int c, n, sign=1;
ifstream ifs("test.txt", std::ifstream::in);
while (ifs.good() && (c=ifs.peek())!=EOF ) {
if (isdigit(c)) {
ifs >> n;
n *= sign;
sign = 1;
cout << n << endl;
}
else {
c=ifs.get();
if (c == '-')
sign = -1;
else sign = 1;
}
}
ifs.close();
It's not the most performant approach, however it has the advantage of only reading from stream, without intermediary strings and memory management.
I was wondering if someone could help me figure out how to read from a text file in C++, character by character. That way, I could have a while loop (while there's still text left) where I store the next character in the text document in a temp variable so I could do something with it, then repeat the process with the next character. I know how to open the file and everything, but temp = textFile.getchar() doesn't seem to work.
You could try something like:
char ch;
fstream fin("file", fstream::in);
while (fin >> noskipws >> ch) {
cout << ch; // Or whatever
}
#cnicutar and #Pete Becker have already pointed out the possibility of using noskipws/unsetting skipws to read a character at a time without skipping over white space characters in the input.
Another possibility would be to use an istreambuf_iterator to read the data. Along with this, I'd generally use a standard algorithm like std::transform to do the reading and processing.
Just for example, let's assume we wanted to do a Caesar-like cipher, copying from standard input to standard output, but adding 3 to every upper-case character, so A would become D, B could become E, etc. (and at the end, it would wrap around so XYZ converted to ABC.
If we were going to do that in C, we'd typically use a loop something like this:
int ch;
while (EOF != (ch = getchar())) {
if (isupper(ch))
ch = ((ch - 'A') +3) % 26 + 'A';
putchar(ch);
}
To do the same thing in C++, I'd probably write the code more like this:
std::transform(std::istreambuf_iterator<char>(std::cin),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(std::cout),
[](int ch) { return isupper(ch) ? ((ch - 'A') + 3) % 26 + 'A' : ch;});
Doing the job this way, you receive the consecutive characters as the values of the parameter passed to (in this case) the lambda function (though you could use an explicit functor instead of a lambda if you preferred).
To quote Bjarne Stroustrup:"The >> operator is intended for formatted input; that is, reading objects of an expected type and format. Where this is not desirable and we want to read charactes as characters and then examine them, we use the get() functions."
char c;
while (input.get(c))
{
// do something with c
}
Here is a c++ stylish function your can use to read files char by char.
void readCharFile(string &filePath) {
ifstream in(filePath);
char c;
if(in.is_open()) {
while(in.good()) {
in.get(c);
// Play with the data
}
}
if(!in.eof() && in.fail())
cout << "error reading " << filePath << endl;
in.close();
}
//Variables
char END_OF_FILE = '#';
char singleCharacter;
//Get a character from the input file
inFile.get(singleCharacter);
//Read the file until it reaches #
//When read pointer reads the # it will exit loop
//This requires that you have a # sign as last character in your text file
while (singleCharacter != END_OF_FILE)
{
cout << singleCharacter;
inFile.get(singleCharacter);
}
//If you need to store each character, declare a variable and store it
//in the while loop.
Re: textFile.getch(), did you make that up, or do you have a reference that says it should work? If it's the latter, get rid of it. If it's the former, don't do that. Get a good reference.
char ch;
textFile.unsetf(ios_base::skipws);
textFile >> ch;
Assuming that temp is a char and textFile is a std::fstream derivative...
The syntax you're looking for is
textFile.get( temp );
There is no reason not to use C <stdio.h> in C++, and in fact it is often the optimal choice.
#include <stdio.h>
int
main() // (void) not necessary in C++
{
int c;
while ((c = getchar()) != EOF) {
// do something with 'c' here
}
return 0; // technically not necessary in C++ but still good style
}
I have a question on the stream behavior, see the following example. What I was expecting is, since there are only 5 chars in the string, and stream read will get stuck as I am trying to read 10 chars. Instead, the output is "hellooooo" ... the last char get repeated.
My questions are two folds: first, why? second, is there anyway to make stream behave as if no more repeating of last char?
#include <sstream>
#include <iostream>
using namespace std;
int main(void) {
char c;
string msg("hello");
istringstream iss(msg);
unsigned int i = 0;
while (i < 10) {
iss >> c;
cout << c;
i++;
}
cout << endl;
return 0;
}
What you see is the result of reading form a stream in an erronous state. When you read past the last element in the stream (this being a string stream), the stream becomes erroneous and any other attempt to read from it will fail (and leave the extraction variable untouched).
You will have to check if the extraction operation succeeded before reading further:
if (iss >> c) {
// succeess
} else {
// failed to extract, handle error
}
Were you to use a stream connected to the console (for an example) your call to >> would have blocked as you expected. The behavior of stringstream is different (you cannot expect to micraculously contain more data)
The reason is that when you've read to the end of the stream, all attempts to read after that just fail, leaving the last value read in your c.
If you want to read at most 10 characters:
while (i < 10 && is >> c) {
cout << c;
i++;
}
This works because a stream can be converted to bool, and it's true if the stream is in a "good" state.
"the last char get repeated"
When iss >> c fails, c stays unmodified.
Check whether extraction of value succeeded by directly evaluating this expression: if (iss >> c), but don't even think about calling iss.good(). Check this answer and also have a look at:
How does that funky while (std::cin >> foo) syntax work?
Why does my input seem to process past the end of file?