I'm having a hard time understanding why while (cin.get(Ch)) doesn't see the EOF. I read in a text file with 3 words, and when I debug my WordCount is at 3 (just what I hoped for). Then it goes back to the while loop and gets stuck. Ch then has no value. I thought that after the newline it would read the EOF and break out. I am not allowed to use <fstream>, I have to use redirection in DOS. Thank you so much.
#include <iostream>
using namespace std;
int main()
{
char Ch = ' ';
int WordCount = 0;
int LetterCount = 0;
cout << "(Reading file...)" << endl;
while (cin.get(Ch))
{
if ((Ch == '\n') || (Ch == ' '))
{
++WordCount;
LetterCount = 0;
}
else
++LetterCount;
}
cout << "Number of words => " << WordCount << endl;
return 0;
}
while (cin >> Ch)
{ // we get in here if, and only if, the >> was successful
if ((Ch == '\n') || (Ch == ' '))
{
++WordCount;
LetterCount = 0;
}
else
++LetterCount;
}
That's the safe, and common, way to rewrite your code safely and with minimal changes.
(Your code is unusual, trying to scan all characters and count whitespace and newlines. I'll give a more general answer to a slightly different question - how to read in all the words.)
The safest way to check if a stream is finished if if(stream). Beware of if(stream.good()) - it doesn't always work as expected and will sometimes quit too early. The last >> into a char will not take us to EOF, but the last >> into an int or string will take us to EOF. This inconsistency can be confusing. Therefore, it is not correct to use good(), or any other test that tests EOF.
string word;
while(cin >> word) {
++word_count;
}
There is an important difference between if(cin) and if(cin.good()). The former is the operator bool conversion. Usually, in this context, you want to test:
"did the last extraction operation succeed or fail?"
This is not the same as:
"are we now at EOF?"
After the last word has been read by cin >> word, the string is at EOF. But the word is still valid and contains the last word.
TLDR: The eof bit is not important. The bad bit is. This tells us that the last extraction was a failure.
The Counting
The program counts newline and space characters as words. In your file contents "this if fun!" I see two spaces and no newline. This is consistent with the observed output indicating two words.
Have you tried looking at your file with a hex editor or something similar to be sure of the exact contents?
You could also change your program to count one more word if the last character read in the loop was a letter. This way you don't have to have newline terminated input files.
Loop Termination
I have no explanation for your loop termination issues. The while-condition looks fine to me. istream::get(char&) returns a stream reference. In a while-condition, depending on the C++ level your compiler implements, operator bool or operator void* will be applied to the reference to indicate if further reading is possible.
Idiom
The standard idiom for reading from a stream is
char c = 0;
while( cin >> c )
process(c);
I do not deviate from it without serious reason.
you input file is
this is fun!{EOF}
two spaces make WordCount increase to 2
and then EOF, exit loop! if you add a new line, you input file is
this is fun!\n{EOF}
I took your program loaded it in to visual studio 2013, changed cin to an fstream object that opened a file called stuff.txt which contains the exact characters "This is fun!/n/r" and the program worked. As previous answers have indicated, be careful because if there's not a /n at the end of the text the program will miss the last word. However, I wasn't able to replicate the application hanging in an infinite loop. The code as written looks correct to me.
cin.get(char) returns a reference to an istream object which then has it's operator bool() called which returns false when any of the error bits are set. There are some better ways to write this code to deal with other error conditions... but this code works for me.
In your case, the correct way to bail out of the loop is:
while (cin.good()) {
char Ch = cin.get();
if (cin.good()) {
// do something with Ch
}
}
That said, there are probably better ways to do what you're trying to do.
Related
I have a weird problem when I test C++ STL features.
If I uncomment the line if(eee), my while loop never exits.
I'm using vs2015 under 64-bit Windows.
int i = 0;
istream& mystream = data.getline(mycharstr,128);
size_t mycount = data.gcount();
string str(mycharstr,mycharstr+mycount);
istringstream myinput(str);
WORD myfunclist[9] = {0};
for_each(myfunclist,myfunclist+9, [](WORD& i){ i = UINT_MAX;});
CALLEESET callee_set;
callee_set.clear();
bool failbit = myinput.fail();
bool eof = myinput.eof();
while (!failbit && !eof)
{
int eee = myinput.peek();
if (EOF == eee) break;
//if (eee) // if i uncomment this line ,the failbit and eof will always be false,so the loop will never exit.
{
myinput >> myfunclist[i++];
}
//else break;
failbit = myinput.fail();
eof = myinput.eof();
cout << myinput.rdstate() << endl;
}
I think that
int eee = myinput.peek();
at some point returns zero.
Then due to
if (eee)
you stop reading from the stream and never reach EOF.
Try to do
if (eee >= 0)
instead
As an alternative you could do:
if (eee < 0)
{
break;
}
// No need for further check of eee - just do the read
myinput >> myfunclist[i++];
The root cause of your problem is a misunderstanding about the way streams set their flags: fail() and eof() are only set once a reading operation fails or tried to read after the last byte was reached.
In other words, with C++ streams you may perfectly have read the last byte of your input and be at the end of file, yet eof() will stay false until you try to read more. You will find on StackOverflow many questions and answers about why you should not loop on eof in a C++ stream.
Consequences:
You will always enter into the loop, even if there is no character to read in myinput.
You therefore have to check for the special case of peek() returning EOF.
If you're still in the loop after the peek, then there are still characters to read. Keep in mind that peek() does not consume the characters. If you do not read it in a proper way, you stay at the same position in the stream. So if for any reason you do no reach myinput >> myfunclist[i++];, you're stuck in an endless loop, constantly peeking the same character over and over again. This is the 0 case that is well described in 4386427's answer : it's still there and you do not progress in the stream.
Other comments:
since your input can be 128 bytes long, and you read integers in text encoding, you could have evil input with 64 different words, causing your loop to go out ov bounds and cause for example memory corruption.
It is not clear why at all you try to peek.
I'd suggest to forget about the flags, use the usual stream reading idiom and simplify the code to:
...
callee_set.clear(); // until there, no change
while (i<9 && myinput >> myfunclist[i++])
{
cout << myinput.rdstate() << endl; // if you really want to know ;-)
}
This code is written in C++ and for reasons that I don't quite understand it is written twice.
I would expect that after inputting a random char it would display the char once and the String lower to it once as well. But I don't get this as output. What am I missing?
Solution: Adding a cin.ignore() statement disregards the return that is read in as well.
Making my code go through the loop once.
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
char letter;
letter = cin.get();
while (letter!= 'X')
{
cout << letter << endl;
cout << "this will be written twice for ununderstandable reasons";
letter = cin.get();
}
}
Example:
If I were to write in cmd scrn c, I'd get a c back + twice the phrase this will be written twice for ununderstandable reasons. So what I thought to be the output
c
this will be written twice for ununderstandable reasons
is actually
c
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
as everyone already mentioned, cin will append the newline marker \n every time you hit enter. another solution is to place cin.ignore(); after every cin.get();.
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
char letter;
letter = cin.get();
cin.ignore();
while (letter!= 'X')
{
cout<<letter<<endl;
cout<<"this will be written twice for ununderstandable reasons";
letter= cin.get();
cin.ignore();
}
}
You are reading every character with the unformatted get() function, including the newline character each time you hit return.
Depending on what you're trying to do, you could use formatted input (cin >> c) to skip all whitespace; or you could test each character and ignore things like newline that don't interest you; or you could use getline(cin, some_string) to read a whole line, and then process that.
When you type in a character the new-line character (from pressing enter) is also in your input buffer.
From the C-Reference:
The delimiting character is not extracted from the input sequence if found, and remains there as the next character to be extracted from the stream (see getline for an alternative that does discard the delimiting character).
Just use a cin.sync() after every cin.get() to clear the buffer and you should be good to go.
You forgot about the newline.
cin reads every character, which includes the newline you type after typing your character.
If you don't want this behaviour, you have to specifically check for newline.
while (letter!= 'X')
{
if (letter == '\n')
{
letter = cin.get();
continue;
}
cout<<letter<<endl;
cout<<"this will be written twice for ununderstandable reasons";
letter= cin.get();
}
The text 'this will be written twice..' will not necessarily print twice.
Type 'qwerty' + ENTER and your stream will have "qwerty\n" within and you'll see this output:
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
this will be written twice for ununderstandable reasons
Exactly that many as string "qwerty\n" has characters.
The problem is that
cin.get()
Puts all chars that you type into a stream/buffer (not your letter char) but handles one char every cin.get() invocation.
When you type 'abcXd' + enter - the program will print above line 3 times and stop on X.
It happens because cin.get() reads new-line character too. Try to press Enter without any symbols or type some string, like abc.
You need to handle it, e.g.:
while (letter = cin.get()) {
if (!isalpha(letter)) { continue; }
// handling user inputted alpha
}
Hi i was just wondering if anybody could help me i am reading characters from a file then inserting them into a map i have the code working i was just wondering how do i detect if a space is in the file cause i need to store the amount of times a space occurred in a file any help would be great thanks.
map<char, int> treeNodes; //character and the frequency
ifstream text("test.txt");
while(!text.eof())
{
text >> characters;
//getline(text,characters);
cout << characters;
if(treeNodes.count(characters) == 0)
{
if(isspace (characters))
{
cout << "space" << endl;
}
else
treeNodes.insert(pair<char,int>(characters,1));
}
else
{
treeNodes[characters] += 1;
}
}
Formatted input, i.e. when using the right shift operator>>() skips leading whitespace by default. You can turn this off using std::noskipws but depending on what sort of things you want to read it won't be a very happy experience. The best approach is probably using unformatted input, i.e. something like std::getline() and split the line on space within the program.
If you just want to count the number of times any particular character occurred, you probably want to use std::istreambuf_iterator<char> and just iterate over the content of the stream (this code also omits some other unnecessary clutter):
for (std::istreambuf_iterator<char> it(text), end(); it != end; ++it) {
++treeNodes[*it];
}
BTW, you never want to use the result of eof() for something different than determining whether the last read failed because the stream has reached its end.
couldn't you just cast the char to an int and test if it is equal to the ascii value of a space?
I recently wrote a program that takes inputted char data, tests if it is acceptable (a-z, # marks the end of the input) and puts it in a stack which then tests to see if it's a palindrome. I was expecting to enter it one char by a time, but if I enter a string ended by pound it.. works. Here is some of the relevant code:
char buffer;
bool pound_test = false;
bool palindrome = false;
bool keep_going = true;
stack<char> stack1, stack2, stack3;
string str = "";
cout << "Please enter a string, then end it with the pound sign. " << endl;
while(pound_test == false) {
cin >> buffer;
if((buffer >= 97) && (buffer <= 122))
{
stack1.push(buffer);
stack2.push(buffer);
str += buffer;
}
if((buffer >= 65) && (buffer <= 90)) {
buffer = buffer + 32;
stack1.push(buffer);
stack2.push(buffer);
str += buffer;
}
if(buffer == '#')
pound_test = true;
}
So, when the user enters one long string, like "racecar#" and presses enter, the program properly puts it into the stack. My question is simply: why? Wouldn't the data have to be inputted one char at a time for it to work properly, because the cin is in the loop itself, and the loop has to repeat to enter multiple chars into the stack, right? Thanks!
Edit: Thanks for the answers/comments everyone! I'm really impressed by the quick and kind replies. I'm certainty going to use this site again.
Console input (via the cin std::istream object) in most systems is line buffered. So when you call cin::operator>> for a single character, the function does not in fact return until you press newline (because the underlying I/O system does not make data available to cin until then). Any data entered up-to and including the <newline> will be buffered and subsequent calls to cin::operator>> will be serviced from the buffer until it is exhausted.
In this case cin >> buffer, where buffer is of type char will indeed get a single character, but before that the console buffered an entire line and will use it to satisfy subsequent console input operations.
If you step through your code in your debugger the operation may be clearer to you.
The "system" (OS, library, whatever — depends on the implementation) ate the string of data coming from input, but your program read it char by char.
While all the answers about os buffering are true, I think the confusion can be traced to cin's operator >> (char); because C++ can overload methods based on their argument types, the char version of operator >> is only assigning one character at a time, even though the whole string is buffered. I believe you're thinking that operator >> should try to put the whole string into your character; but since it "knows" you're reading one character at a time, it only assigns one character at a time. I'm not sure if this is specified behavior for cin or not, but that seems to be what's happening.
The cin operator reads from the standard input stream (if not configured otherwise). The stdin works as follows: you type and when you press Enter, it is sent to stdin and therefore cin reads the whole string up to the moment when you pressed Enter.
If you wish to read char by char, you should use getchar.
The way your keyboard input is seen by cin >> buffer;is not a property of your program, but of the combination of OS, Shell, C runtime and maybe thousand things I forgot.
I'm need to know if the next char in ifstream is the end of file. I'm trying to do this with .peek():
if (file.peek() == -1)
and
if (file.peek() == file.eof())
But neither works. There's a way to do this?
Edit: What I'm trying to do is to add a letter to the end of each word in a file. In order to do so I ask if the next char is a punctuation mark, but in this way the last word is left without an extra letter. I'm working just with char, not string.
istream::peek() returns the constant EOF (which is not guaranteed to be equal to -1) when it detects end-of-file or error. To check robustly for end-of-file, do this:
int c = file.peek();
if (c == EOF) {
if (file.eof())
// end of file
else
// error
} else {
// do something with 'c'
}
You should know that the underlying OS primitive, read(2), only signals EOF when you try to read past the end of the file. Therefore, file.eof() will not be true when you have merely read up to the last character in the file. In other words, file.eof() being false does not mean the next read operation will succeed.
This should work:
if (file.peek(), file.eof())
But why not just check for errors after making an attempt to read useful data?
file.eof() returns a flag value. It is set to TRUE if you can no longer read from file. EOF is not an actual character, it's a marker for the OS. So when you're there - file.eof() should be true.
So, instead of if (file.peek() == file.eof()) you should have if (true == file.eof()) after a read (or peek) to check if you reached the end of file (which is what you're trying to do, if I understand correctly).
For a stream connected to the keyboard the eof condition is that I intend to type Ctrl+D/Ctrl+Z during the next input.
peek() is totally unable to see that. :-)
Usually to check end of file I used:
if(cin.fail())
{
// Do whatever here
}
Another such way to implement that would be..
while(!cin.fail())
{
// Do whatever here
}
Additional information would be helpful so we know what you want to do.
There is no way of telling if the next character is the end of the file, and trying to do so is one of the commonest errors that new C and C++ programmers make, because there is no end-of-file character in most operating systems. What you can tell is that reading past the current position in a stream will read past the end of file, but this is in general pretty useless information. You should instead test all read operations for success or failure, and act on that status.
You didn't show any code you are working with, so there is some guessing on my part. You don't usually need low level facilities (like peek()) when working with streams. What you probably interested in is istream_iterator. Here is an example,
cout << "enter value";
for(istream_iterator<double> it(cin), end;
it != end; ++it)
{
cout << "\nyou entered value " << *it;
cout << "\nTry again ...";
}
You can also use istreambuf_iterator to work on buffer directly:
cout << "Please, enter your name: ";
string name;
for(istreambuf_iterator<char> it(cin.rdbuf()), end;
it != end && *it != '\n'; ++it)
{
name += *it;
}
cout << "\nyour name is " << name;
just use this code in macosx
if (true == file.eof())
it work for me in macosx!