Break out of an overloaded extraction operator? (C++) - c++

I'm trying to use an overloaded ">>" to scan input from a file.
The problem is, I have no idea how to deal with end of file.
In this case, my file is composed of a number, followed by several chars
Ex:
9rl
8d
6ff
istream &operator>>(istream &is, Move &move)
{
char c;
int i = 0;
c = is.get();
if (!isalnum(c))
return;
move.setNum(c); // I convert the char to an int, but I'l edit it out
while ( (c = is.get()) != '\n')
{
move.setDirection(i, c); //sets character c into in array at index i
i++;
} // while chars are not newline
return is;
} // operator >>
The test for the character being alpha numeric worked when I had this as a regular function, but doesn't work here as it expects an input stream to be returned. I've tried returning NULL as well. Suggestions?
EDIT: this is being called in a while loop, So i'm trying to figure out some way to have this trigger some flag so that I can break out of the loop. In my previous function, I had this return a boolean, returning true if successful or false if the character was not alphanumeric

Return is. Callers should check the stream for errors.
Be sure to set error bits as appropriate:
std::istream &operator>>(std::istream &is, Move &move)
{
char c;
int i = 0;
c = is.get();
if (is.eof())
return is;
else if (c < '0' || c > '9') {
is.setstate(std::ios::badbit);
return is;
}
else
move.setNum(c-'0');
while ( (c = is.get()) != '\n' && is)
move.setDirection(i++, c);
if (c != '\n')
is.setstate(std::ios::badbit);
return is;
}
Use it as in the following:
int main(int argc, char **argv)
{
std::stringstream s;
s << "9rl\n"
<< "8d\n"
<< "6ff\n";
s.seekg(0);
Move m;
while (s >> m)
std::cout << m;
if (s.bad())
std::cerr << argv[0] << ": extraction failed\n";
return 0;
}
Notice that the code uses the instance m only after successful extraction.

You can set the flags of the stream to a state such as ios::bad or ios::fail using ios::setstate. This will allow the caller to test the stream or in the case that exceptions are enabled for the stream, an exception will be raised.
You also fail to check the state of your stream. The C++ FAQ lite has a great section explaining this. To clarify this I have added the code snippet below.
c = is.get();
// the stream has not been tested to see if it read into c correctly
if (!isalnum(c))
return;

Related

How to replace Hi with Bye in a file

I want to replace hi with a bye by reading a file and outputting another file with the replaced letters.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream myfile;
ofstream output;
output.open("outputfile.txt");
myfile.open("infile.txt");
char letter;
myfile.get(letter);
while (!myfile.eof()) {
if (letter == 'H') {
char z = letter++;
if (z == 'i')
output << "BYE";
}
else output << letter;
}
output.close();
myfile.close();
return 0;
}
My outputs are repeated capital I's that is repeated infinity times.
Here is my input file
Hi
a Hi Hi a
Hi a a Hi
Don't check eof
The eof method is returning the location of the input stream read pointer, and not the status of the get. It is more like telling you whether or not get will succeed, so you could write something like:
while (!myfile.eof()) {
char letter;
myfile.get(letter);
//...
}
In this way, you would at least be getting a new letter at each iteration, and the loop ends when the read pointer reaches the end of the input.
But, there are other cases that might cause the get to not succeed. Fortunately, these are captured by the stream itself, which is returned by get. Testing the status of the stream is as easy as treating the stream as a boolean. So, a more idiomatic way to write the loop is:
char letter;
while (myfile.get(letter)) {
//...
}
Peek at the next letter
When you want to look at the next letter in the input following the detected 'H', you perform an increment.
char z = letter++;
But, this does not achieve the desired result. Instead, it just sets both letter and z variables to the numerical successor of 'H' ('H' + 1), and does not observe the next letter in the input stream.
There is another method you can use that is like get, but leaves the input in the input stream. It is called peek.
char z;
auto peek = [&]() -> decltype(myfile) {
if (myfile) z = myfile.peek();
return myfile;
};
if (peek()) {
//...
}
And now, you can check the value of z, but it is still considered input for the next get on letter.
Close to what you implemented
So, the complete loop could look like:
char letter;
while (myfile.get(letter)) {
if (letter == 'H') {
char z;
auto peek = [&]() -> decltype(myfile) {
if (myfile) z = myfile.peek();
return myfile;
};
if (peek() && z == 'i') {
myfile.get(z);
output << "BYE";
continue;
}
}
output << letter;
}
With this approach, you will be able to correctly handle troublesome cases like HHi as input, or the last letter in the input being an H.
Your two lines:
myfile.get(letter);
while (!myfile.eof()) {
are wrong.
First off you only read letter once, hence your infinite loop.
Secondly you don't use eof in a while loop.
You want something more like:
while (myfile.get(letter)) {
Also:
char z = letter++;
is wrong, you want to read another letter:
myfile.get(z);
but you have to be careful that you get something, so
if(!myfile.get(z)) {
output << letter;
break;
}
So finally:
char letter;
while (myfile.get(letter)) {
if (letter == 'H') {
char z;
if(!myfile.get(z)) {
output << letter;
break;
}
if (z == 'i') {
output << "BYE";
}
else output << letter << z;
}
else output << letter;
}
But now we are consuming the character after any H which may not be desirable.
See #jxh's answer for a way to do this with look ahead.
There is a dedicated function to replace patterns in strings. For example, you could use std::regex_replace. That is very simple. We define, what should be searched for and with what that would be replaced.
Some comments. On StackOverflow, I cannot use files. So in my example program, I use a std::istringstream instead. But this is also an std::istream. You can use any other std::istream as well. So if you define an std::ifstream to read from a file, then it will work in the same way as the std::istringstream. You can simply replace it. For the output I use the same mechanism to show the result on the console.
Please see the simple solution:
#include <iostream>
#include <sstream>
#include <regex>
// The source file
std::istringstream myfile{ R"(Hi
a Hi Hi a
Hi a a Hi)" };
// The destination file
std::ostream& output{ std::cout };
int main() {
// Temporary string, to hold one line that was read from a file
std::string line{};
// Read all lines from the file
while (std::getline(myfile, line)) {
// Replace the sub-string and write to output file
output << std::regex_replace(line, std::regex("Hi"), "Bye") << "\n";
}
return 0;
}

(C++) How to check whether or not an input string is an integer?

I want this code to check whether the input is an int or not, and it works fine until I enter a float type number. I need a program that won't let through float numbers.
bool error;
int x;
string s;
do
{
cout <<"number: ";
cin >> x;
error=cin.fail();
if(error)
{
cout << "error" <<endl;
cin.clear();
}
getline(cin,s);
}while(error);
cout << x;
Read in all of the user's input line as a string, then convert the string to an integer with std::stoi.
Unfortunately std::stoi will happily stop converting when it hits the end of convertible characters, but it allows you to pass in a pointer to a position to be updated with the character that ended the conversion. If this position is not the end of the string, there was garbage on the line.
bool error = true; // assume user input is wrong
while (error)
{
if (std::getline(std::cin, s)) // grab the whole line
{
std::size_t end;
try
{
x = std::stoi(s, &end);
if (end == s.length()) // converted all user input
{
error == false; // good data
}
}
catch(std::invalid_argument &) // user input is complete garbage
{
}
catch(std::std::out_of_range &) // converted user input is too big for int.
{
}
}
}
^
I recommend turning the input loop into a function. 1) It's easily reusable should you need to convert an int again. 2) It gets rid of some of the logic above because you can return when the input is tested and good.
int gimmieInt(std::istream& in) // Me eat input stream! Om nom nom nom!
{
std::string s;
int x;
while (true) // consider instead using a maximum number of retries.
// This increases complexity, so consider it *after* you have
// the basic version working
{
if (std::getline(in, s))
{
std::size_t end;
try
{
x = std::stoi(s, &end);
if (end == s.length())
{
return x;
}
}
catch(std::invalid_argument &) // user input is complete garbage
{
}
catch(std::std::out_of_range &) // user input is too big for int.
{
}
}
}
}
std::istream& operator(std::istream&, int) will read a valid integer number up to any non matching character like '.', and no error state is set for the stream up to this point.
You better should read complete (whitespace separeated) chunks as std::string, and inspect them if these contain the desired format (e.g. using std::regex).
std::stoi() should also fail with an exception, if you're trying to convert the chunk.
I think, you are looking for something like this (C++11):
auto s = std::string{};
std::cin >> s;
if( std::all_of(std::begin(s), std::end(s),
[](char c) -> bool {
return c <= '0' && c <= '9';
}) ) {
std::cout << "you have entered an integer" << std::endl;
}
Somehow I thought, that the standard library contains a predicate that checks if a given char is a digit, but I could not find it now. Such a hypothetic is_digit() would allow make the code more readable:
if( std::all_of(std::begin(s), std::end(s), std::hypothetic::is_digit) ) {
std::cout << "you have entered an integer" << std::endl;
}

Questions related to reading in different format of numbers characters

How I can read in those elements and store them into two different arrays., i.e. : the number in <?> and the word in [?] to be separately read in and stored.
<98>
Avs [adadada]
<45>
[adafaf] BBBHADA
asdadqd aada [Mammoth]
<-1> // ends the read
The rest of the info in the file is useless which do not require to be stored.
Edit:
Following the advice of one of the answers, here is my first attempt:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string.h>
using namespace std;
int main(int argc, char* argv[])
{
if(argc != 3)
cout<< "Error! Not enough file!"<<endl;
int** page = new int*[];
char** words = new char*[];
}
//---------------------------------------------------------
void readInput(int** page1, char** name1){
istream is;
char par1, par2;
int usefulVal;
is >> par1 >> usefulVal >> par2;
// check if any input
if(!is) return is;
// check for valid input format
if (par1 != '<' || par2 != '>'){
// set failbit to indicate invalid input format
is.clear(ios_base::failbit);
// assign input values to second argument
page1(usefulVal);
char par_1, par_2;
string string_value;
is >> par1 >> string_value >> par2;
if(!is) return is;
if (par_1 != '[' || par_2 != ']')
{
is.clear(ios_base::failbit);
return is;
}
name1(string_value);
return is;
}
Question:
1. Is there a way to read and store the above elements separately?
2. What am I doing wrong?
P.S.: I'm just trying out some C++. Hope someone can shed some light on it, thanks!
To specify your format and read only specific elements you could overload the operator >>. For example, the first format: < int_value >, could be implemented with:
istream& operator>>(istream& is, class_name& array_name){
char par2, par2;
int int_value;
is >> par1 >> int_value >> par2;
// check if any input
if(!is) return is;
// check for valid input format
if (par1 != '<' || par2 != '>'){
// set failbit to indicate invalid input format
is.clear(ios_base::failbit);
return is;
}
// assign input values to second argument
array_name(int_value);
// chain for consecutive use
return is;
}
and the second format: [ string_value ] with:
istream& operator>>(istream& is, class_name& separate_array_name){
char par2, par2;
string string_value;
is >> par1 >> int_value >> par2;
if(!is) return is;
if (par1 != '[' || par2 != ']'){
is.clear(ios_base::failbit);
return is;
}
separate_array_name(string_value);
return is;
}
Note:
The second parameter in both examples: class_name& array_name and class_name& separate_array_name are not real types and are left for you to decide/ implement.
Edit:
The function you've defined is not used in your main().
The function needs to be either defined before the main() or forward declared.
Limit the excessive use of pointers and dynamically allocated memory, as it needs to be taken care of (freed) at the end.
Look up how to use istream: file name, modes, etc.
Prefer simple functions, i.e. doing one single process. It is much more easier to implement and detect errors.

Issues with characters at the end of an istream

I'm writing a parser, and I was previously having trouble when I try to parse identifiers (anything that's valid for a C++ variable name) and unclosed string literals (anything starting with ", but missing the closing ") at the end of my input. I think it's because the lexer (TokenStream) uses std::noskipws in these cases and builds the token character by character. Here is where I believe I have narrowed down the problem (shown only for one of the two cases, as the other is very similar logic):
std::string TokenStream::get()
{
char c;
(*input) >> c; // input is of type istream*
// other cases...
if (c == '"')
{
std::string s = stringFromChar(c); // just makes a string from the char.
char d;
while (true) // 1)
{
(*input) >> std::noskipws >> d;
std::cout << d; // 2)
if (d == '"')
{
s += d;
(*input) >> std::skipws;
break;
}
s += d;
}
return s;
}
// other cases...
}
Note that this function is supposed to just generate tokens from the input in a stream-like fashion. Now, if I input either a literal (like asdf) or an unclosed string (like "asdf), then the program will hang, and the line marked 2) will just output the last character of the input (in my examples, f) over and over again forever.
I've solved this problem by using a check for input->eof(), but my question is this:
Why does the loop (marked 1) in comments) keep executing when I hit the end of stream, and why does it just print that last character read every time through the loop?
Lets look at the loop in question line-by-line
while (true) // 1)
That's gonna loop, unless a break is encountered
{
(*input) >> std::noskipws >> d;
Read a character. If can't read character, d is likely to be unchanged.
std::cout << d; // 2)
Print the character that is just read
if (d == '"')
Nope, the last character was not " (as specified in the question)
{
s += d;
(*input) >> std::skipws;
break;
}
s += d;
}
Therefore the break is never encountered and the last character is printed in an endless loop.
Fix: always use a while look like this for input:
char ch;
while (input >> ch) {
// ch contains a new letter, deal with it
}

checking of the stream result?

I have two questions on the following simple code:
/*
test.cpp
© BS.
Example of creation of a class of a flow for which it is possible to assign the additional
characters interpreted as separators.
11/06/2013, Раздел 11.7
*/
//--------------------------------------------------------------------------------------------------
#include <iostream>
#include <sstream>
#include <algorithm>
#include <exception>
#include <string>
#include <vector>
using namespace std;
//--------------------------------------------------------------------------------------------------
class Punct_stream
// it is similar to istream flow, but the user can independently set separators.
{
private:
istream& source; // source of chars
istringstream buffer; // buffer for formatting
string white; // whitespaces
bool sensitive; // case sensitive
public:
Punct_stream(istream& is): source(is), sensitive(true) {}
void whitespace(const string& s) { white = s; }
void add_white(char c) { white += c; }
void case_sensitive(bool b) { sensitive = b; }
bool is_case_sensitive() { return sensitive; }
bool is_whitespace(char c);
Punct_stream& operator >> (string& s);
operator bool();
};
//--------------------------------------------------------------------------------------------------
bool Punct_stream::is_whitespace(char c)
// is the c a whitespace?
{
for(int i = 0; i < white.size(); ++i) if(c == white[i]) return true;
return false;
}
//--------------------------------------------------------------------------------------------------
Punct_stream::operator bool()
// check the input result
{
return !(source.fail() || source.bad()) && source.good();
}
//--------------------------------------------------------------------------------------------------
Punct_stream& Punct_stream::operator >> (string& s){
while(!(buffer >> s)){ // try to read the data from the buffer
if(buffer.bad() || !source.good()) return *this;
buffer.clear();
string line;
getline(source,line); // read the line from the source
// if necessary we replace characters
for(int i = 0; i < line.size(); ++i)
if(is_whitespace(line[i])) line[i] = ' ';
else if(!sensitive) line[i] = tolower(line[i]);
buffer.str(line); // write a string line to the stream
}
return *this;
}
//==================================================================================================
int main()
// entry point
try{
Punct_stream ps(cin);
ps.whitespace(";:,.?!()\"{}<>/&$##%^*|~");
ps.case_sensitive(false);
cout << "Enter the words, please: ";
vector<string> vs;
string word;
while(ps >> word) vs.push_back(word); // enter words
sort(vs.begin(), vs.end());
// we delete counterparts
for(int i = 0; i < vs.size(); ++i) if(i == 0 || vs[i] != vs[i-1]) cout << vs[i] << endl;
}
catch(exception& e){
cerr << e.what() << endl;
return 1;
}
catch(...){
cerr << "Unknown exception." << endl;
return 2;
}
The checking in the Punct_stream::operator bool() function is not clear for me:
return !(source.fail() || source.bad()) && source.good();
My questions:
Why author checked the 'fail' and the 'bad'? Why it wasn't restricted the 'good' check only? Unless the positive 'good' automatically doesn't imply, what 'fail' and 'bad' are set in 'false' value?
Besides, often in a code write such construction:cin >> x; while(cin){//...}
Why the author didn't write by analogy so:
Punct_stream::operator bool()
// check the input result
{
// return !(source.fail() || source.bad()) && source.good();
return source;
}
The alternative option shown by me doesn't work for me (Windows crashed), it would be desirable to understand why, what I missed?
Thank you.
The istream object contains a bunch of bit flags that represent its internal status. Of them, the ones that interests you are:
eofbit End-Of-File reached while performing an extracting operation on an input stream.
failbit The last input operation failed because of an error related to the internal logic of the operation itself.
badbit Error due to the failure of an input/output operation on the stream buffer.
goodbit No error. Represents the absence of all the above (the value zero).
Their status is represented in the following table
good() eof() fail() bad()
goodbit No errors (zero value iostate) true false false false
eofbit End-of-File reached on input operation false true false false
failbit Logical error on i/o operation false false true false
badbit Read/writing error on i/o operation false false true true
Respectively, good() returns the goodbit, eof() checks the eofbit, fail() checks the failbit, and, surprisingly, bad() returns the badbit.
So depending on what you are doing, each function could be used in a different settings.
However, in your case, simply checking the trueness of the good bit would be enough, as it is true when the others are all false. Testing at the same time the failbit or the badbit is redundant.
source: http://www.cplusplus.com/reference/ios/ios/
EDIT:
Actually I'm not really sure why your alternative wouldn't work, as it works for me. What data did you exactly pass to the program?