Why does getline behave weirdly after 3 newlines? - c++

I'll preface this by saying I'm relatively new to posting questions, as well as C++ in general, my title is a little lame as it doesn't really specifically address the problem I am dealing with, however I couldn't really think of another way to word it, so any suggestions on improving the title is appreciated.
I am working on a relatively simple function which is supposed to get a string using getline, and read the spaces and/or newlines in the string so that it can output how many words have been entered. After reaching the character 'q' it's basically supposed to stop reading in characters.
void ReadStdIn2() {
std::string userInput;
const char *inputArray = userInput.c_str();
int count = 0;
getline(std::cin, userInput, 'q');
for (int i = 0; i < strlen(inputArray); i++){
if ((inputArray[i] == ' ') || (inputArray[i] == '\n')){
count += 1;
}
}
std::cout << count << std::endl;
}
I want to be able to enter multiple words, followed by newlines, and have the function accurately display my number of words. I can't figure out why but for some reason after entering 3 newlines my count goes right back to 0.
For example, if I enter:
hello
jim
tim
q
the function works just fine, and returns 3 just like I expect it to. But if I enter
hello
jim
tim
bill
q
the count goes right to 0. I'm assuming this has something to do with my if statement but I'm really lost as to what is wrong, especially since it works fine up until the 3rd newline. Any help is appreciated

The behaviour of the program is undefined. Reading input into std::string potentially causes its capacity to increase. This causes pointers into the string to become invalid. Pointers such as inputArray. You then later attempt to read through the invalid pointer.
P.S. calculating the length of the string with std::strlen in every iteration of the loop is not a good idea. It is possible to get the size without calculation by using userInput.size().
To fix both issues, simply don't use inputArray. You don't need it:
for (int i = 0; i < userInput.size(); i++){
if ((userInput[i] == ' ') || (userInput[i] == '\n')){
...

Related

I can check if a string is null-terminated but not check it isn't yet null terminated

For the sake of me better understanding C++ strings, array and pointers; I want to know: Why is it that I can use a condition whereby I check if the index has reached the null-terminating character like this...
const char* myString = "Grandopolous";
for (int i = 0;;i++)
{
if (!myString[i])
break;
else
cout << myString[i];
}
So that works just fine. Here I am instead checking to see if the character equals something other than the null-terminating character and so I expect that if it doesn't the result should be not 0 and the condition should be true. but this does not work, and I simply cannot fathom why:
const char* myString = "Grandopolous";
for (int i = 0;;i++)
{
if (myString[i])
cout << myString[i];
}
This does not work on my machine and crashes, also it outputs a lot of unreadable error messages mixed with strange symbols. I don't think that part matters although it is the first time error have been printed to my console application instead of the debug console.
The reason I mentioned pointers is because I managed to get the condition to work using pointers instead of the array index syntax which I find much easier to read.
So could someone please help me understand why my first bit of code is valid and why my second is not.
It does work. The check for null isn't the problem.
Your program crashes because you got rid of the break so your program overruns the array then continues forever into the abyss.
Your debugger would surely have revealed this to you as you stepped through the program, observing i.
To reverse the logic of your first example, write:
const char* myString = "Grandopolous";
for (int i = 0;;i++)
{
if (myString[i])
cout << myString[i];
else
break;
}

Detecting if the user enters space or enter in a conditional with cin.get()

I'm trying to get this input loop to stop if the user enters a space ' ' or enter \n but the space part doesn't seem to work, even when I replace in != ' ' with in != 32 which is the ASCII code for space.
#include<iostream>
using namespace std;
int main()
{
int temp, cho = 0;
char in = '0';
while (in != '\n' && in != ' ')
{
in = cin.get();
temp = in - '0';
if (temp >= 0 && temp <= 9)
cho += temp;
}
}
Is it even possible to achieve with cin.get() and a console application?
You actually have three problems here that won't be independently distinguishable from each other to begin with, but which become clear once you apply the reasoning below:
I. Boolean conditional is incorrect
while (in != '\n' || in != ' ')
This is wrong! You probably meant &&.
(A != B || A != C) is always true (assuming B and C are different, as they are in your example) because A cannot possibly equal both of them at the same time.
II. Program logic is in the wrong order
Furthermore, you're checking this in the wrong place. You're doing this:
Set input #0 to '0' to get us going
Does the input #0 meet my exit criteria? (no)
Take input #1
Deal with input #1
Does the input #1 meet my exit criteria? (no)
Take input #2 (say this is a space)
Deal with input #2
Does the input #2 meet my exit criteria? (YES!)
End the loop
You see how you check input #2 too late? It's already been "dealt with". You'll exit the loop just fine after implementing the above fix, but you've already appended the character to cho.
How about this:
int temp, cho = 0;
// Get input for the first iteration
char in = cin.get();
while (in != '\n' && in != ' ')
{
temp = in - '0';
if(temp >=0 && temp <=9)//so only the ASCII of digits would be entered
cho += temp;
// Now get input for the next iteration
in = cin.get();
}
The duplication isn't nice, but you can fiddle with it as you please once the logic's correct.
III. Your terminal has line buffering turned on
Finally, even with this code you may experience problems due to line buffering in your terminal: your program will be functioning absolutely correctly, but since your characters are often by default not sent to the program until a whole line is provided, there is no "live"/"instant" reaction to the act of pressing a space. Only once you hit enter are all those characters finally submitted to your program by your terminal, at which point the backlogged spaces trigger the loop exit; this makes it look like your program is only terminating on the newline condition, but it's not. You might have spotted this if you had generated some output from within your program to see how many characters it was actually processing before quitting.
You can resolve this by turning off line buffering in your terminal emulator, or by removing the ability to use spaces to terminate the loop and instead just rely on newlines — the latter is the convention as then you do not have to ask your users to configure their terminal specially to run your program: it'll already function properly in all the usual cases.
Bootnote — general advice
It's important not to assume that, if applying solution A for problem 1 doesn't immediately make your program work perfectly, that solution A must be wrong. You should consider the possibility that you also have as-yet-unknown problems 2 and 3.
It's really important to keep an open mind and gather evidence, such as writing output from your program to track its execution... or use a debugger to step through it and analyse what it is doing. As far as I can tell, you haven't really gathered any evidence at all about how your program executes… beyond cursory empirical observations, that is.
Keeping Lightness's great answer in mind, it should be noted that you're reading a single whitespace-separated token, which the built-in formatted I/O operators already do. For example, operator>>() is designed to pull out a token of input (say a number) until it reaches whitespace which is a space character and newline character.
A cleaner way of doing it would be to use the standard algorithms and classes from the standard library such as std::istream_iterator and std::accumulate():
#include <iterator>
#include <string>
#include <iostream>
#include <numeric>
int main()
{
typedef std::istream_iterator<std::string> iter_t;
iter_t it(std::cin);
int cho(0);
if (it != iter_t{})
{
auto s = *it;
cho = std::accumulate(std::begin(s), std::end(s), 0,
[] (int v, unsigned char c) { return v + c - '0'; });
}
std::cout << cho;
}
std::istream_iterator uses operator>>() internally and std::accumulate() will go through the characters, convert them to integers, and accumulate their sum.

Simple C++ not reading EOF

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.

How to put the string entered into a character array in C++?

Now, I am facing with such a problem: Compare two strings without using "strcmp" in library function.
I have defined the function "mystrcmp" correctly, but I also have to put the string entered into a character array. How can I realize it?
Here is my wrong codes:
char a1[100],a2[100];
int j=0;
do
{
cin>>a1[j];
j=j+1;
}while(getchar()!=10);
int k=0;
do
{
cin>>a2[k];
k=k+1;
}while(getchar()!=10);
cout<<j<<" "<<k<<"\n";
I want to see if the loops are correct through j and k. Unfortunately, the results are wrong.
For example, when I enter "abcdefg" and "gfedcba", I get the result "j=4, k=4".
What's wrong with my codes? How can I correct it?
I'm looking forward to your answers. Thank you.
Why are you using the value 10 in your code? Don't use integer literals in place of character constants, because when you attempt to run this code on a computer that uses the EBCDIC character set you'll notice that '\n' has the value 37, not 10. Use '\n' instead of 10.
Don't mix getchar and cin code. That's a pretty bad idea, because they both consume one character each. In other words, getchar() is consuming one byte, and cin is consuming one byte, so you're consuming two bytes per loop and only storing one of those bytes. If you're going to use getchar, I think you mean something like this:
for (int c = getchar(); c >= 0 && c != '\n'; c = getchar()) {
a1[j++] = c;
}
a1[j] = '\0';
The same sort of thing using C++'s cin:
for (int c = cin.get(); cin.good() && c != '\n'; c = cin.get()) {
a1[j++] = c;
}
a1[j] = '\0';
This is dangerous code. You can write out of a1 & a2 bounds. Use functions made for this, for example cin.getline
http://www.cplusplus.com/reference/istream/istream/getline/

Entering a string of characters using arrays and pointers

Ok guys, i'm very beginner and trying to enter string to a char array using pointers..and then display what i've written.
There're two things i want to ask about. First , if i didn't want to specify a size for the array and just want it to expand to contain all string i've entered ..how is that ?
And second after i enter the string and display it...it won't contain the SPACE between word...
like if i entered "i love cookies"...it will be displayed as "ilovecookies"..So how to solve that ?
Here's my little code ...
#include <iostream>
using namespace std;
int main()
{
char *strP , str[100] ;
strP = str ;
for(int i =0 ; i<10 ; i++) cin >> *(strP+i) ;
for(int i =0 ; i<10 ; i++) cout << *(strP+i) ;
return 0;
}
sorry for my silly questions, I'm beginner to this language as said and don't want to miss things before moving on .
Thanks in advance .
1) You need to either use a string object or new if you want to dynamically resize your string.
2) It doesn't contain the spaces because cin reads one words at a time. There are several ways to get around this. The one I would use is switch to using scanf and printf instead of cin and cout. Or, as vivin said, you can use getchar()
EDIT: grammar
cin always stops when it encounters a space. If you're entering character by character, try using getchar().
Arrays can't change their size. You should use std::vector<char>, or even better for strings you would use std::string.