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.
Related
everyone, here is a function I wrote to read a user input which is a vector of double of unknown size, the input must terminate when 'enter' is pressed:
vector<double> read_array()
{
vector<double> array_in;
double el;
while (!cin.get())
{
cin >> el;
array_in.push_back(el);
}
return array_in;
}
To illustrate it consider the following code:
void init() // the function that calls the read_array function
{
cout << "Enter array X: " << endl;
vector<double> X = read_array();
int l = X.size();
cout << l << endl;
}
A typical input when promted is:
1(space)2(space)3(space)4(enter)
When enter is pressed, the input terminates, and the variable 'l' is initialised but is equal to 0
However, when the enter key is pressed, the array size is 0. Debugging it makes it look like it never makes it into the loop like that.
The same routine works well if the input value is not an array.
Thanks to everyone in advance!
I don't know what you hope std::cin.get() does but based on your comment it seems you hope that it somehow deals with end of lines: it doesn't. It simply reads the next character which is unlikely to do you much good. In particular, if the character is anything but '\0' negating it will result in the boolean value false. That said, the loop should in principle work unless you only input a single digit numeric value followed (possibly after space) by a non-digit or the end of the input.
The easiest approach to deal with line-based input is to read the line into a std::string using std::getline() and then to parse the line using std::istringstream:
std::vector<double> read_array() {
std::vector<double> result;
if (std::string line; std::getline(std::cin, line)) {
std::istringstream lin(line);
for (double tmp; std::cin >> tmp; ) {
result.push_back(tmp);
}
}
return result;
}
As std::cin is only involved while reading lines, std::cin.fail() won't be set when parsing doubles fails. That is, you can read multiple lines with arrays of doubles, each of which can also be empty.
If you don't want to read an auxiliary line, you'll need to understand a bit more about how formatted input in C++ works: it starts off skipping whitespace. As newlines are whitespace you need to rather read the whitespace yourself and stop if it happens to be a newline or non-whitespace. I'd use a function doing this skipping which returns false if it reached a newline (which is still extracted):
bool skip_non_nl_ws(std::istream& in) {
for (int c; std::isspace(c = in.peek()); std::cin.ignore()) {
if (c == '\n') {
return false;
}
}
return true;
}
std::vector<double> read_array() {
std::vector<double> result;
for (double tmp; skip_non_nl_ws(std::cin) && std::cin >> result); ) {
result.push_back(tmp);
}
return result;
}
This approach has a similar property that std::ios_base::failbit won't be set. However, if any of the characters on a line can't be parsed as double the bit will set. That way you can detect input errors. The approach using std::getline() will just go on to the next line.
This question already has answers here:
How to test whether stringstream operator>> has parsed a bad type and skip it
(5 answers)
Closed 7 years ago.
I'm a beginner having trouble with a common issue. I'm trying to read standard input line by line in C++ wherein each line is [integer][comma][integer]. For example:
1,1
2,3
12,5
etc. I want to only use the integers in each line and assign them to separate variables. The part of my code that's hassling me looks something like this:
int x,y;
for (string line; getline(cin, line); ) {
// ...want to have something like
// x = first integer, y = second integer
// process x and y
}
So essentially, I don't care about storing the values for the integers on each line, but I do want to process each pair of integers to compare them or whatnot.
I've noticed other solutions that use vectors, stringstream, tokens and/or delimiters, but those were almost always for input lines with multiple delimiters per line - here we only have 1 per line, so I thought there might be a more simple solution.
Thanks for your time and patience.
Try this:
#include <iostream>
#include <sstream>
#include <string>
// ...
for (std::string line; std::getline(std::cin, line); )
{
std::istringstream iss(line);
int x, y;
char c;
if (!(iss >> x >> c >> y >> std::ws) || iss.get() != EOF || c != ',')
{
std::cerr << "Cannot parse line '" << line << "'.\n";
continue;
}
std::cout << "Input: " << x << ", " << y << "\n";
}
(This logic actually allows more lenient input (regarding whitespace) than you give in your example; if you want the matching to be strict, you have to not skip whitespace.)
To read from standard input, you can use the cin command. Read the input as a string and parse it accordingly to get the input.
However, your input here as a predefined format, and we can use that to our advantage by using scanf. Your code will be as follows:
int x,y;
while (scanf("%d,%d",&x,&y) == 1)
{
...
...
}
The %d,%d implies that scanf is looking for an input in the form of an integer, followed by a comma, followed by an integer. The scanf(...) == 1 will evaluate to false if the input is not in the format specified, and will help in exiting the while loop if the user presses Enter twice (or gives some other input).
I have been working on this for a while and can't fix it. I am very new to C++. So far I can get 10 things into my array but the output is not legible, it's just a bunch of numbers. I have read other posts with similar code but for some reason mine isn't working.
The input text file is 10 lines of fake data like this:
56790 "Comedy" 2012 "Simpsons" 18.99 1
56791 "Horror" 2003 "The Ring" 11.99 7
My code is here:
(My output is below my code)
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct DVD {
int barcode;
string type;
int releaseDate;
string name;
float purchaseprice;
int rentaltime;
void printsize();
};
int main () {
ifstream in("textfile.exe");
DVD c[10];
int i;
for (int i=0; i < 10; i++){
in >> c[i].barcode >> c[i].type >> c[i].releaseDate >>
c[i].name >> c[i].purchaseprice >> c[i].rentaltime;
}
for (int i=0;i< 10;i++) {
cout << c[i].barcode<<" ";
cout << c[i].type<<" ";
cout << c[i].releaseDate<<" ";
cout << c[i].name << " ";
cout << c[i].purchaseprice << " ";
cout << c[i].rentaltime << "\n";
}
return 0;
}
My output looks similar to garbage, but there are 10 lines of it like my array:
-876919876 -2144609536 -2.45e7 2046
A comment on what to study to modify my code would be appreciated.
As suggested by cmbasnett, ifstream in("textfile.exe") reads in an executable file. If you with for the program to read in a text file, changing it to ifstream in("textfile.txt") should work.
You always need to check that your input is actually correct. Since it may fail prior to reading 10 lines, you should probably also keep a count of how many entries you could successfully read:
int i(0);
for (; i < 10
&& in >> c[i].barcode >> c[i].type >> c[i].releaseDate
>> c[i].name >> c[i].purchaseprice >> c[i].rentaltime; ++i) {
// ???
}
You actual problem reading the second line is that your strings are quoted but the approach used for formatted reading of strings doesn't care about quotes. Instead, strings are terminated by a space character: the formatted input for strings will skip leading whitespace and then read as many characters until another whitespace is found. On your second line, it will read "The and then stop. The attempt to read the purchaseprice will fail because Ring isn't a value numeric value.
To deal with that problem you might want to make the name quotedstring and define an input and output operators for it, e.g.:
struct quoted_string { std::string value; };
std::istream& operator>> (std::istream& in, quoted_string& string) {
std::istream::sentry cerberos(in); // skips leading whitespace, etc.
if (in && in.peek() == '"') {
std::getline(in.ignore(), string.value, '"');
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
std::ostream& operator<< (std::ostream& out, quoted_string const& string) {
return out << '"' << string.value << '"';
}
(note that the code isn't test but I'm relatively confident that it might work).
Just to briefly explain how the input operator works:
The sentry is used to prepare the input operation:
It flushes the tie()d std::ostream (if any; normally there is none except for std::cin).
It skips leading whitespace (if any).
It checks if the stream is still not in failure mode (i.e., neither std::ios_base::failbit nor `std::ios_base::badbit are set).
To see if the input starts with a quote, in.peek() is used: this function returns an int indicating either that the operation failed (i.e., it returns std::char_traits<char>::eof()) or the next character in the stream. The code just checks if it returns " as it is a failure if the stream returns an error or any other character is present.
If there is a quote, the quote is skipped using file.ignore() which by default just ignores one character (it can ignore more characters and have a character specified when to stop).
After skipping the leading quote, std::getline() is used to read from file into string.value until another quote is found. The last parameter is defaulted to '\n' but for reading quoted string using a '"' is the correct value to use. The terminating character is, conveniently, not stored.
When I load less than 5 chars, it is ok. But if i load more than five chars my program crash.
How can i protect before that?
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
char tab[5];
int tab2[5];
char *wsk = tab;
int i = 0;
cin >> tab;
while (true) {
cin >> tab2[i];
if (tab2[i] == 0) break;
i++;
}
i = 0;
while (true) {
if (tab2[i] ==0) break;
wsk += tab2[i];
cout << *wsk;
i++;
}
return 0;
}
You don;t want to limit it to 5.
What you really want is to make sure the read works and never crashes.
The reason you don't want to stop reading at 5 characters is that if the user enters more than 5 characters you have stopped reading in the middle of their input and you now have to write code to find the end of this input and then continue. Writting code to fix the input stream is hard. Rather take input validate (the user may have typed crap and you can generate an error message) but you will be in the correct place to continue reading for the next input operation.
char tab[5];
cin >> tab; // Fails if you read more than 4 input characters
// (because it will add '\0' on the end)
Why not use a self expanding destination structure.
std::string tab;
std::cin >> tab; // Read one word whatever the size.
But what about the array.
No more difficult. Here you want an array that re-sizes. Guess what we have std::vector
int tab2[5];
while (true) {
cin >> tab2[i]; // Fails on the 6 number you input.
// STUFF
}
The loop can be written like this:
std::vector<int> tab2;
while (true) {
int val;
cin >> val;
tab2.push_back(val);
// STUFF
}
Instead of:
while (true)
put:
while (i < 5)
For C-style arrays, you have to set the width of the input stream to the number of characters allotted to the buffer, otherwise it will be possibly for you to write past the end of the array and get a buffer overflow. This is typically done using ios_base::width:
std::cin.width(5);
std::cin >> buffer;
You can also use the manipulator std::setw:
std::cin >> std::setw(5) >> buffer;
These will both set the maximum width of the stream to 5 characters. The width will be reset to its default after the first input operation.
Your loop condition should be
while(i < 5)
Also a for loop would fit perfectly
for(int i = 0; i < 5; i++) {
// body
}
You can use the algorithms part of the STL to bound your reads.
for example :
int main(){
char c[5];
auto newEnd = std::copy_n(std::istream_iterator<char>(std::cin), 5, std::begin(c));
// if newEnd != c + 5 then it failed to read 5 characters
}
The standard input is a stream. You don't get to decide what's in it. All you can do is read from it and see what you get -- either you get some data, or you learn that the stream has ended.
If you really only want to read five bytes, you can use std::cin.read(tab, 5); you must then call std::cin.gcount() to see how many bytes have actually been read and only consume as many bytes.
Alternatively, you can use C++'s dynamic containers and use std::getline(std::cin, line) to read into an std::string line as much data as is available up to a newline.
In either event, you first do the reading, then check whether and how much you actually read, and then check if what you read is of the form you expected (e.g. alphanumeric).
Ok, I'm trying to get good at using pointers so I'm trying to write a input validation for the user input to make sure that anything that isn't a number is handled correctly. When I use isdigit() isn't working for me. I still get an exception when I enter a alphabet. Any suggestions? Thanks. Check this out:
#include<iostream>
#include<algorithm>
#include<string>
#include<cctype>
using namespace std;
void EnterNumbers(int * , int);
int main()
{
int input = 0;
int *myArray;
cout << "Please enter the number of test scores\n\n";
cin >> input;
//Allocate Array
myArray = new int[input];
EnterNumbers(myArray,input);
delete[] myArray;
return 0;
}
void EnterNumbers(int *arr, int input)
{
for(int count = 0; count < input; count++)
{
cout << "\n\n Enter Grade Number " << count + 1 << "\t";
cin >> arr[count];
if(!isdigit(arr[count]))
{
cout << "Not a number";
}
}
}
If you test if (!(cin >> arr[count])) ... instead - isdigit(arr[digit]) tests if the value of arr[digit] is the ASCII code of a digit [or possibly matches Japanese, Chinese or Arabic (that is, as an Arabic script typeface, not that it's a 0-9 like our "Arabic" ones) digit]. So if you type in 48 to 57, it will say it's OK, but if you type 6 or 345, it's complaining that it is not a digit...
Once you have discovered a non-digit, you will also need to either exit or clean out the input buffer from "garbage". cin.ignore(1000, '\n'); will read up to the next newline or a 1000 characters, whichever happens first. Could get annoying if someone has typed in a million digits, but otherwise, should solve the problem.
You will of course also need a loop to read the number again, until a valid number is entered.
The way I do this kind of input validation is that I use std::getline(std::cin, str) to get the whole line of input and then I parse it using the following code:
std::istringstream iss(str);
std::string word;
// Read a single "word" out of the input line.
if (! (iss >> word))
return false;
// Following extraction of a character should fail
// because there should only be a single "word".
char ch;
if (iss >> ch)
return false;
// Try to interpret the "word" as a number.
// Seek back to the start of stream.
iss.clear ();
iss.seekg (0);
assert (iss);
// Extract value.
long lval;
iss >> lval;
// The extraction should be successful and
// following extraction of a characters should fail.
result = !! iss && ! (iss >> ch);
// When the extraction was a success then result is true.
return result;
isdigit() applies to char not to int as you're trying. The cin >> arr[count]; statement already ensures an integer numeric digits format is given in the input. Check cin.good() (!cin respectively) for possible input parsing errors.