I got conflicting advice with respect to how c++ operates with respect to reading past the eof.
The first group of people state that when the marker is reading past the eof area it reaches the eof and stops while the other group of people state it has to be in the exact position for it to be processed as reaching the eof. To make this clearer let me paste 2 blocks of code.
In this block of code, I am reading a number 1 from the file numbers.txt. They're no syntax errors and the only thing which I didn't paste over here is the code that opens the file.
while (!sample.eof())
{
char ch;
sample.get(ch);
sample.seekp(-1L, ios::cur);
sample >> initialnumber;
sample.seekp(2L, ios::cur);
cout << "OK";
}
In this program here I am reading the number 1 moving back one space making it start from the beginning processing it and then moving two spaces forward. The output for this is OK written only once.
#include < iostream>
#include < fstream>
#include< string>
using namespace std;
string conversion(int);
int conversion2(string);
int main()
{
string initialnumber;
fstream SAMPLE("numbers.txt", ios::in | ios::out);
ofstream sample2("numbers2.txt");
if (sample && sample2)
{
int number2;
string roman;
int number;
char ch;
while (!sample.eof()) {
sample.get(ch);
if (ch != '1' && ch != '2' && ch != '3' && ch != '4' && ch != '5' && ch != '6'
&& ch != '7' && ch != '8' && ch != '9') {
SAMPLE.seekg(-1L, ios::cur);
sample >> roman;
sample.seekg(2L, ios::cur);
sample2 << roman << " " << conversion2(roman) << endl;
int L = sample.tellp();
cout << L;
}
else {
sample.seekg(-1L, ios::cur);
sample >> number2;
sample2 << conversion(number2) << " " << number2 << endl;
sample.seekg(2L, ios::cur);
}
}
}
else
{
cout << "fail";
}
sample.close();
sample2.close();
}
Here it is repeating the number infinite number of times when it shouldn't be repeating it meaning it never reached the eof.
Please help me understand the logic of both programs.
Starting from C++11
Before doing anything else, seekg clears eofbit.
http://en.cppreference.com/w/cpp/io/basic_istream/seekg
Since seekg is always the last thing you call before checking .eof(), it will never be detected. The eofbit is set when a read operation hits the end of file. So, a read must be the last thing you do for it to work.
Related
I have looked in several places on the Internet but cannot find what I am looking for. Basically I am trying to understand data validation and filter out all user input except either the number 1 or 2. I have found information for validating ints. Found stuff on filtering out chars and strings. But when I try to put them together it doesn't work. Basically if the user enters something that is not 1 or 2, it does not end a loop asking for correct input.
I have included more details in the comments in the code below.
Any help is appreciated!
#include <iostream>
#include <string>
int main()
{
std::cout << "Please enter 1 or 2. No other numbers or characters."
<< std::endl;
std::string numberString;
//Used a string so if the user enters a char it gets converted to an
//integer value of 0.
getline(std::cin, numberString);
int numberInteger = atoi(numberString.c_str());
//If the user enters the wrong number, char, or string,
//the program goes to this area of code.
//But if a subsequent correct entry is made, the loop does not end.
if (numberInteger < 1 || numberInteger > 2)
{
do
{
//Tried using these two lines of code to clear the input buffer,
//but it doesn't seem to work either:
//std::cin.clear();
//std::cin.ignore(std::numeric_limits <std::streamsize>::max(), '\n');
std::cout << "Invalid input. Please enter 1 or 2. No other numbers or characters."
<< std::endl;
getline(std::cin, numberString);
int numberInteger = atoi(numberString.c_str());
} while (numberInteger < 1 || numberInteger > 2);
}
else
{
std::cout << "You entered either 1 or 2. Great job! "
<< std::endl;
}
return 0;
}
#include <cctype>
#include <limits>
#include <iostream>
std::istream& eat_whitespace(std::istream& is)
{
int ch;
while ((ch = is.peek()) != EOF && ch != '\n' &&
std::isspace(static_cast<char unsigned>(ch))) // 0)
is.get(); // As long as the next character
// is a space, get and discard it.
return is;
}
int main()
{
int choice;
while (std::cout << "Please enter 1 or 2. No other numbers or characters: ",
!(std::cin >> std::skipws >> choice >> eat_whitespace) || // 1)
std::cin.peek() != '\n' || // 2)
choice < 1 || 2 < choice) { // 3)
std::cerr << "I said 1 or 2 ... nothing else ... grrr!\n\n";
std::cin.clear(); // 4)
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 5)
}
std::cout << "Input was " << choice << '\n';
}
0) Don't feed isspace() negative values.
1) Extraction of an int failed. Allow whitespace before and after the int.
2) If the next character in the stream is not a newline character, there is garbage left eat_whitespace() didn't swallow --> complain.
3) choice not in range.
4) clear flags to make sure input functions will work again.
5) ignore up to maximum streamsize characters untill the next newline.
This is an example of the c++ reference page of
std::basic_streambuf::sputbackc
and i am trying to find:
Why does " ch = pbuf->sbumpc() " ask me for input just one time in a loop and not in every cycle?
here is the code:
#include <iostream> // std::cin, std::cout, std::streambuf, std::streamsize
int main () {
char ch;
std::streambuf * pbuf = std::cin.rdbuf();
std::cout << "Please, enter some letters and then a number: ";
do {
ch = pbuf->sbumpc(); //why this line ask an input just once in all the loop?
if ( (ch>='0') && (ch <='9') )
{
pbuf->sputbackc (ch);
long n;
std::cin >> n;
std::cout << "You entered number " << n << '\n';
break;
}
} while ( ch != std::streambuf::traits_type::eof() );
return 0;
}
Lets say you enter LL10. pbuf->sbumpc(); reads one character from the stream. So it reads in L. Since that is not between 0 and 9 we loop again. L10 is still in the buffer so we read in L again. Again it it not in the range so we loop again. Now the buffer just has 10 in it. We read in the 1 and since it is in the range we put it back, read in the buffer using std::cin >> n; and then break out of the loop. That is why you only have to enter input once. Had you only inputted LL it would wait for you to enter more.
void displayFile()
{
char userInput[20];//Used to store the typed input commands from the user.
ifstream ip("D:\\H Drive ITT\\Level 8 Degree\\Software Dev (Andrew)\\MiniProject\\about.txt");//Input from file
string line ;//Store each line in the string
int exit = 0;//Used to hold the value which indicates whether or not the function should end determined by the user.
if (ip)//ip is true if the file exists.
{}
else
{
cout << "-Error_File_Not_Found" << endl;
}
while (!ip.eof())//Scan till end of file
{
ip >> line;
cout << line << endl;
}
cout << "Type exit to return" << endl << endl << ">>>" ;
do {
cin >> userInput;
if (strcmp(userInput, "exit") == 0 || strcmp(userInput, "EXIT") == 0 || strcmp(userInput, "eXIT") == 0 || strcmp(userInput, "Exit") == 0)
{
exit = 1;
}
else
{
exit = 0;
}
} while (exit !=1);
ip.close();
}
//The
//output
//displays
//like
//this.
When the code runs it takes in the paragraph and displays each word on a line of its own. I have seen solutions that involve classes but I need a different solution as I have not done classes yet.
There are two problems here.
The >> operator does not read lines, it stops on whitespace. Use std::getline as Ramana suggested in the comments.
You are using eof() incorrectly. It returns true after you have already read past the end of the file, not before you would read past the end. So your loop will iterate one time too many. The best method is to use the return value of std::getline as the loop condition.
I'm writing this program for class. It's supposed to take in a word 10 or less chars in size and change each char individually to uppercase using a toupper() call. The requirements of the assignment are to use toupper() on each individual char in an array that holds the word and the '\n'. Here is my code:
int main(int argc, char *argv[])
{
char again = ' ';
char word[11];
for(int count=0; count<11; count++){
word[count] = ' ';
}
while(true){
for(int clear=0; clear < 11; clear++){
word[clear] = ' ';
}
system("CLS");
cout << "Please provide a lowercase word!" << endl;
cin.getline(word, 11);
for(int letter = 0; letter < 11; letter++){
system("CLS");
if(letter < 10){
word[letter] = toupper(word[letter]);
}
for(int printw = 0; printw < 11; printw++){
cout << word[printw];
}
Sleep(200);
}
cout << endl;
while(true){
cout << "Would you like to Repeat or Quit? (R/Q)" << endl;
cin.get(again);
cin.get();
tolower(again);
if(again == 'r' || again == 'q'){
break;
}
else{
cout << "That was an invalide input!" << endl;
}
}
if(again == 'q'){
break;
}
}
system("PAUSE");
return EXIT_SUCCESS;
}
The program works just fine for 1 - 10 char words, but if the user decides to enter a string larger than 10 chars, the program will uppercase the first 10 and then hit an infinite loop when it asks if the user would like to try again. When that happens it will continue to return "That was an invalid output!" and the next prompt to repeat or quit on the next line. It will not wait for input, and I've tried clearing the buffer with cin.ignore().
I tried checking the values in the buffer with several cout << cin.get() << endl; line and they all come back with the ascii value of -1. I assume this means it is throwing a failbit exception, but I'm not actually sure.
Instead of using a character array of fixed size, you should use a string to store your input. You can use getline to read a line from cin like this:
string input;
...
getline(cin, input);
This will read the entire line, regardless of how long it is or how many characters you actually process.
What you're seeing here is the expected behaviour of cin.getline(char* s, streamsize n) From C++ ref for istream::getline:
The failbit flag is set if the function extracts no characters, or if
the delimiting character is not found once (n-1) characters have
already been written to s.
If a user types a word longer than 10 chars, then cin writes the first 10 chars to word and sets its internal flag to fail as no delimiter \n (newline) has been seen.
If you want to stick to your current solution, you could overcome this with:
const int SIZE = 11;
...
cin.getline(word, SIZE);
if (cin.fail() && cin.gcount() == SIZE-1) { // failed because user entered a word longer than 10 chars
word[SIZE-1] = '\0'; // make sure to null terminate
cin.clear(); // clear the failure from cin
cin.ignore(256, '\n'); // ignore the leftover input from cin until next newline
}
You need to both clear the failbit and ignore the leftover input. 256 is simply a number large enough, to be super safe you can use cin.ignore(numeric_limits<streamsize>::max(), '\n'); (don't forget to #include <limits>)
PS You might be better off reading a char at a time with cin.get(char& c) though.
I have looked over this for hours it seems like. This program will compile, it just can't detect errors correctly. And for some reason it will work when I type in hey [) or hey {], etc. But it won't work for hey[) or hey{]. Obviously in all cases it should detect an error but for some reason the space after 'hey' makes a difference.
#include<iostream>
#include <stack>
using namespace std;
bool delimiterMatching(char *file){
stack<char> x;
int count = 0;
char ch, onTop, check;
while(ch != '\0'){
ch = file[count];
if (ch == '(' || ch == '[' || ch == '{')
x.push(ch);
else if (ch == ')' || ch == ']' || ch == '}') {
onTop == x.top();
x.pop();
if((ch==')' && onTop!='(') || (ch==']' && onTop!='[') || (ch=='}' &&
onTop!= '{'))
return false;
}
count++;
}
if (x.empty())
return true;
else
return false;
}
int main()
{
char *test = new char();
cout << "enter sentence: ";
cin >> test;
if (delimiterMatching(test))
cout << "success" << endl;
else
cout << "error" << endl;
return 1;
}
With cin >> test you don't get a whole sentence, but only a string until cin encounters whitespace. So if you type (hey ), thest would be (hey and the closing brace would only be read by the next >>, whereas (hey) would work as expected.
You have a second issue with your test allocation, which might be too short for reasonable input.
Change main() as follows:
char *test = new char[256]; // enough space. COnsider also string
cout << "enter sentence: ";
cin.getline(test, 256); // full line input.
...
You have also two nasty bugs in delimiterMatching().
First you use an uninitialized ch in your while condition. Either initialise ch to a non nul char, or use while (file[count]).
And did you notice onTop == x.top(); ? Shouldn't it be onTop = x.top();?