How do I quit a while(scanf_s...) loop? - c++

My Visual Studio tells me to use scanf_s(), and now that I've used it, my while loop goes on forever. Let's say I'm reading lines, where each line has two numbers separated with a space. Once I've inserted all the numbers, my while never stops. How do I quit my loop?
int main() {
int i,j;
while (scanf_s("%d %d", &i, &j)) {
int maxLength = 0;
for (int index = i; index <= j; index++) {
int tmp = cycle(index);
if (tmp > maxLength) {
maxLength = tmp;
}
}
printf("%d %d %d\n", i, j, maxLength);
}
cout << "lol";
return 0;
}

My Visual Studio tells me to use scanf_s,
Dubious advice, but standard in MS-land (as an alternative to scanf).
and now that i've used it my
while goes on forever.
That's not a distinction between scanf_s and scanf. Both return the number of fields successfully converted and assigned. Both will keep waiting indefinitely for input until they see non-matching data or the end of the file. Both return EOF, which is nonzero, when the end of the file is reached without scanning any fields.
Let's say i'm reading lines, where each line
has to numbers seperated with a space. Once ive inserted all the
numbers my while never stops. How do i quit my loop?
Test for the specific expected return value, not its general truthiness:
while (scanf_s("%d %d", &i, &j) == 2) { // ...
If you're reading from a regular file then that's sufficient by itself, but if you're reading an indefinite amount of interactive input then you must rely on the user to provide some kind of indicator that they have no more to provide. They can send an end-of-file signal (<ctrl-Z> on Windows), or in this case, they could also enter a line containing a character that is neither whitespace nor a decimal digit nor a '+' or '-' (which will remain unread in the input).

scanf_s returns the number of fields translated on success. On error it returns either 0 or EOF.
Your logic is assuming that any non-zero value is success. When the return value is EOF, that assumption breaks.
Try comparing against the number of fields you need:
while (2 == scanf_s("%d %d", &i, &))

Related

How to check if scanf input is a number? (user input has to be a float)

I´d like to check if user input is a float/integer or a letter. For this purpose the use of scanf() is important. My problem is that a letter is saved as zero but I can´t exclude it.
I tried building solving it with if and as a condition I tried isalpha() and is digit().I tried a bunch of other things, but it´s not worth mentioning.
If the actual requirement is something like this:
Read a series of numbers (integers or floating-point numbers) from the user. Stop reading when the user types something other than a number, such as a letter.
, then the solution, using scanf, is perfectly straightforward:
#include <stdio.h>
int main()
{
int maxnums = 10;
double numbers[maxnums];
int i;
for(i = 0; i < maxnums; i++) {
printf("enter a number, or nondigit to quit: ");
fflush(stdout);
double x;
if(scanf("%lf", &x) != 1)
break;
numbers[i] = x;
}
int nnum = i;
printf("you typed %d number(s):\n", nnum);
for(i = 0; i < nnum; i++)
printf("%d: %f\n", i, numbers[i]);
}
The loop stops (using the break statement; I hope you've gotten to that in class by now) if scanf returns something other than 1, indicating that no number could be read. The loop also stops after the user enters 10 numbers, because that's how big the input array is.
It looks like the loop reads only floating-point numbers, and in a sense it does, but if the user types a pure integer, like 123, without a decimal point, that's still perfectly acceptable as a floating-point input, and it will be read as the floating-point value 123.000.
On the other hand, if there was a requirement to be able to tell whether the user actually entered a pure integer versus a floating-point number, or if there was a requirement to do something else with the pure integers other than storing them in the same array with the floating-point inputs, or if there was a requirement to know (to read and perhaps process) the specific non-numeric input the user typed to terminate the input loop, none of those additional requirements could be met with this simple code. In fact, in my opinion, meeting any of those additional requirements using scanf is effectively impossible, and some completely different approach, not involving scanf, would be required.
... to check if user input is a float/integer or a letter ...
scanf() is a weak function to use when input is either convertable as a float, integer or character - yet OP reports scanf() is obliged. Requirement to use scanf() is misguided.
First read a line of user input into a string. Be sure to limit input width.
char line[100 + 1];
if (scanf(" %100[^\n]", line) == 1) {
// Successfully read a line
Now process the string. Use strtof(), strtoll() to parse for float, integer. Test the endptr for conversion success.
char *endptr; // Record end of conversion.
errno = 0;
long long ll = strtoll(line, &endptr, 10);
if (endptr > line && errno == 0 && *endptr == 0) {
// Successfully parsed a long int
printf("long long: %ld\n", ll);
} else {
errno = 0;
float f = strtof(line, &endptr);
if (endptr > line && *endptr == 0) {
// Successfully parsed a float
printf("float: %.9g\n", f);
} else {
printf("otherwise <%s>\n", line);
}
}
Additional code useful to detect empty or long lines.

Check scanf formating without conversions

Is there a way how to check if some specific characters were given on input using scanf without using character conversions?
int main(void)
{
if(scanf("{ ["))
printf("GOOD INPUT\n");
else
printf("BAD INPUT\n");
return 0;
}
This code always gives the bad input option (it expects scanf return value to be 1) but interestingly if I enter other than the desired characters it gives the bad input imediately but if I enter it as its specified in the scanf it blows the bad input at me AFTER I enter the whole input.
So it must be awaiting the input to be in that specified format but my question is: How can I check it, without any conversions, and make according action depending on wether the input was entered correctly or not?
You might use the %n conversion of scanf (it sets below into pos the number of characters read so far)
int pos= -1;
if (scanf("{ [%n", &pos) >=0 && pos>0)
printf("GOOD INPUT\n");
else
printf("BAD INPUT");
Beware, the return count of scanf might be implementation specific (it probably stays at 0). But pos get assigned to a positive offset of scanf did get { followed by some (zero or more) space-like characters followed by a [.
However, what you probably want is some lexical analysis and parsing, then scanf is not a good solution. You'll better read the entire line (e.g. with fgets or getline) and parse it later.
See also the man page of scanf(3)
scanf (and cousins) returns a count of the number of successful conversions. If you specify 0 conversions, then its return value will always be 0.
To use scanf for this task, I'd probably use a couple of scanset conversions:
char a[2], b[2];
if (scanf("%1[{] %1[[]", &a, &b) == 2)
printf("Matched");
Or, you could simplify this a little bit:
char a[2];
if (scanf("{ %1[[]", &a) == 1)
Either way, we've specified each scan set to match only one specified character, but it's still a conversion, so we can see whether it succeeded or failed.
Unfortunately, we still have to assign the result somewhere. scanf does support using * like this: "%*s", to tell it to read a string, but not store the result anywhere--but when you do so, that conversion doesn't get counted in the return value, so (much like before) we can't use it to determine whether we got a match or not.
If good input needs to exactly 3 characters: { space [, use "%*1[ ]" to scan a space and "%n", which saves the scan character count, to insure scanning reach the expected end.
int main(void) {
int n = -1;
scanf("{%*1[ ][%n", *n);
if (n >= 0) {
printf("GOOD INPUT\n");
} else {
printf("BAD INPUT\n");
}
return 0;
}
I'd recommend to read a line of input with fgets() first and then parse the buffer. That can leave stdin in a better known state when bad input happens.

Logic for reading rows and columns from a text file (textparser) C++

I'm really stuck with this problem I'm having for reading rows and columns from a text file. We're using text files that our prof gave us. I have the functionality running so when the user in puts "numrows (file)" the number of rows in that file prints out.
However, every time I enter the text files, it's giving me 19 for both. The first text file only has 4 rows and the other one has 7. I know my logic is wrong, but I have no idea how to fix it.
Here's what I have for the numrows function:
int numrows(string line) {
ifstream ifs;
int i;
int row = 0;
int array [10] = {0};
while (ifs.good()) {
while (getline(ifs, line)) {
istringstream stream(line);
row = 0;
while(stream >>i) {
array[row] = i;
row++;
}
}
}
}
and here's the numcols:
int numcols(string line) {
int col = 0;
int i;
int arrayA[10] = {0};
ifstream ifs;
while (ifs.good()) {
istringstream streamA(line);
col = 0;
while (streamA >>i){
arrayA[col] = i;
col++;
}
}
}
edit: #chris yes, I wasn't sure what value to return as well. Here's my main:
int main() {
string fname, line;
ifstream ifs;
cout << "---- Enter a file name : ";
while (getline(cin, fname)) { // Ctrl-Z/D to quit!
// tries to open the file whose name is in string fname
ifs.open(fname.c_str());
if(fname.substr(0,8)=="numrows ") {
line.clear();
for (int i = 8; i<fname.length(); i++) {
line = line+fname[i];
}
cout << numrows (line) << endl;
ifs.close();
}
}
return 0;
}
This problem can be more easily solved by opening the text file as an ifstream, and then using std::get to process your input.
You can try for comparison against '\n' as the end of line character, and implement a pair of counters, one for columns on a line, the other for lines.
If you have variable length columns, you might want to store the values of (numColumns in a line) in a std::vector<int>, using myVector.push_back(numColumns) or similar.
Both links are to the cplusplus.com/reference section, which can provide a large amount of information about C++ and the STL.
Edited-in overview of possible workflow
You want one program, which will take a filename, and an 'operation', in this case "numrows" or "numcols". As such, your first steps are to find out the filename, and operation.
Your current implementation of this (in your question, after editing) won't work. Using cin should however be fine. Place this earlier in your main(), before opening a file.
Use substr like you have, or alternatively, search for a space character. Assume that the input after this is your filename, and the input in the first section is your operation. Store these values.
After this, try to open your file. If the file opens successfully, continue. If it won't open, then complain to the user for a bad input, and go back to the beginning, and ask again.
Once you have your file successfully open, check which type of calculation you want to run. Counting a number of rows is fairly easy - you can go through the file one character at a time, and count the number that are equal to '\n', the line-end character. Some files might use carriage-returns, line-feeds, etc - these have different characters, but are both a) unlikely to be what you have and b) easily looked up!
A number of columns is more complicated, because your rows might not all have the same number of columns. If your input is 1 25 21 abs 3k, do you want the value to be 5? If so, you can count the number of space characters on the line and add one. If instead, you want a value of 14 (each character and each space), then just count the characters based on the number of times you call get() before reaching a '\n' character. The use of a vector as explained below to store these values might be of interest.
Having calculated these two values (or value and set of values), you can output based on the value of your 'operation' variable. For example,
if (storedOperationName == "numcols") {
cout<< "The number of values in each column is " << numColsVal << endl;
}
If you have a vector of column values, you could output all of them, using
for (int pos = 0; pos < numColsVal.size(); pos++) {
cout<< numColsVal[pos] << " ";
}
Following all of this, you can return a value from your main() of 0, or you can just end the program (C++ now considers no return value from main to a be a return of 0), or you can ask for another filename, and repeat until some other method is used to end the program.
Further details
std::get() with no arguments will return the next character of an ifstream, using the example code format
std::ifstream myFileStream;
myFileStream.open("myFileName.txt");
nextCharacter = myFileStream.get(); // You should, before this, implement a loop.
// A possible loop condition might include something like `while myFileStream.good()`
// See the linked page on std::get()
if (nextCharacter == '\n')
{ // You have a line break here }
You could use this type of structure, along with a pair of counters as described earlier, to count the number of characters on a line, and the number of lines before the EOF (end of file).
If you want to store the number of characters on a line, for each line, you could use
std::vector<int> charPerLine;
int numberOfCharactersOnThisLine = 0;
while (...)
{
numberOfCharactersOnThisLine = 0
// Other parts of the loop here, including a numberOfCharactersOnThisLine++; statement
if (endOfLineCondition)
{
charPerLine.push_back(numberOfCharactersOnThisLine); // This stores the value in the vector
}
}
You should #include <vector> and either specific std:: before, or use a using namespace std; statement near the top. People will advise against using namespaces like this, but it can be convenient (which is also a good reason to avoid it, sort of!)

Why does scanf appear to skip input?

I am confused about scanf's behaviour in the following program. scanf appears to input once, and then not input again, until a stream of characters is printed.
Below in a C program
#include<stdio.h>
int main()
{
int i, j=0;
do
{
++j;
scanf("%d", &i);
printf("\n\n%d %d\n\n", i, j);
}
while((i!=8) && (j<10));
printf("\nJ = %d\n", j);
return 0;
}
here, Till i am inputting any integer program works perfectly fine, but when a character is inputted it goes on printing the last inputed value of i and never stops(untill j is 10 when loop exits) for scanf to take next input.
output::
1 <-----1st input
1 1
2 <---- 2nd input
2 2
a <---- character input
2 3
2 4
2 5
2 6
2 7
2 8
2 9
2 10
J = 10
same thing is happening in c++ also.
#include<iostream>
using namespace std;
int main()
{
int i, j=0;
do
{
++j;
cin>>i;
cout<<i<<" "<<j<<"\n";
}
while((i!=8) && (j<10));
cout<<"\nj = "<<j<<"\n";
}
output of c++ program ::
1 <-----1st input
1 1
2 <-----2nd input
2 2
a <------ character input
0 3
0 4
0 5
0 6
0 7
0 8
0 9
0 10
j = 10
only change in c++ is that 0 is being printed instead of last value.
I know here integer values are expected by the program, but i want to know what happens when character is inputted in place of an integer?
what is the reason of all happening above?
When you enter a, then cin >> i fails to read it because the type of i is int to which a character cannot be read. That means, a remains in the stream forever.
Now why i prints 0 is a different story. Actually it can print anything. The content of i is not defined once the attempt to read fails. Similar thing happens with scanf as well.
The proper way to write it this:
do
{
++j;
if (!(cin>>i))
{
//handle error, maybe you want to break the loop here?
}
cout<<i<<" "<<j<<"\n";
}
while((i!=8) && (j<10));
Or simply this (if you want to exit loop if error occurs):
int i = 0, j = 0;
while((i!=8) && (j<10) && ( cin >> i) )
{
++j;
cout<<i<<" "<<j<<"\n";
}
If scanf sees a character in the input stream that doesn't match the conversion specifier, it stops the conversion and leaves the offending character in the input stream.
There are a couple of ways to deal with this. One is to read everything as text (using scanf with a %s or %[ conversion specifier or fgets) and then use atoi or strtol to do the conversion (my preferred method).
Alternately, you can check the return value of scanf; it will indicate the number of successful conversions. So, if scanf("%d", &i); equals 0, then you know there's a bad character in the input stream. You can consume it with getchar() and try again.
You can never expect your users to enter valid things. The best practice is to read the input into a string and try to convert it to integer. If the input is not an integer, you can give an error message to the user.
The problem is that when you enter an input that is not of the expected type (specified by %d for scanf, and the int type for cin>>i;, the inputstream is not advanced, which results in both operations trying to extract the same type of data from the exact same incorrect input (and failing just as well this time around too), thus you will never asked for another input.
To ensure this does not happen you will need to check the return value of both operations (read the manual for how each reports errors). If an error does happen (as when you enter a character), you will need to clear the error, consume the invalid input and try again. I find it better in C++ to read a whole line using std::gtline() instead of int or even std::string when geting input from ther user interactively, so you get into this "infinite" loop you experienced.
You are ignoring the return value. See what the manual says about scanf(3):
RETURN VALUE
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
It fails matching an integer.
You could check the return value of scanf to determine if an integer has been parsed correctly (return should =1). On failure, you have choices: either notify the user of the error and terminate, or recover by reading the next token with a scanf("%s" ...) perhaps with a warning.
For scanf, you need to check its return value to see if the conversion on the input worked. scanf will return the number of elements successfully scanned. If the conversion did not work, it will leave the input alone, and you can try to scan it differently, or just report an error. For example:
if (scanf("%d", &i) != 1) {
char buf[512];
fgets(buf, sizeof(buf), stdin);
printf("error in line %d: got %s", j, buf);
return 0;
}
In your program, since the input is left alone, your loop repeats trying to read the same input.
In C++, you check for failure using the fail method, but the input stream failure state is sticky. So it won't let you scan further without clearing the error state.
std::cin >> i;
if (std::cin.fail()) {
std::string buf;
std::cin.clear();
std::getline(cin, buf);
std::cout
<< "error in line " << j
<< ": got " << buf
<< std::endl;
return 0;
}
In your program, since you never clear the failure state, the loop repeats using cin in a failure state, so it just reports failure without doing anything.
In both cases, you might find it easier or more reliable to work with the input if you would read in the input line first, and then attempt to parse the input line. In pseudocode:
while read_a_line succeeds
parse_a_line
In C, the catch to reading a line is that if it is longer than your buffer, you will need to check for that and concatenate multiple fgets call results together to form the line. And, to parse a line, you can use sscanf, which is similar to scanf but works on strings.
if (sscanf(buf, "%d", &i) != 1) {
printf("error in line %d: got %s", j, buf);
return 0;
}
In C++, for quick low level parsing of formatted input, I also prefer sscanf. But, if you want to use the stream approach, you can convert the string buffer into a istringstream to scan the input.
std::getline(cin, buf);
if (std::cin.fail()) {
break;
}
std::istringstream buf_in(buf);
buf_in >> i;
if (buf_in.fail()) {
std::cout << "error in line " << j
<< ": got " << buf
<< std::endl;
return 0;
}

ASCII and isalpha if statement issue

I am writing a program that takes a user inputted character, such as A, and a user inputted number, such as 7. The program checks the validity of the character, if true runs thru till it gets to this loop inside of a function. I am using ascii decimal for this loop inside of a function. This loop needs to check isalpha and if it is run the code inside the {}'s, it's doing that correctly. The else is not working the way I want and am not sure how to correct it. I need the else (is not alpha) to add a 1 back to the counter in the loop and increase the ascii by 1. If I run it as so, it gives off a retry/ignore/abort error. If I run it without the num++; it runs and stops after the loop ends. So, if you put in a Z and choose 3, it runs thru the loop 3 times and outputs just a Z. Any thoughts on how to fix this?
I need it to output something like: Input: Z Input: 4 it should output: Z A B C to the screen. It needs to ignore other ascii non alpha characters.
Thanks
string buildSeries(char A, int num)
{
//builds the output with the info the
//user inputted
stringstream str1;
string outted;
int DeC=(int)A, i = 0;
//loop builds the output
for(i=0;i<num;i++)
{
if (isalpha(DeC))
{
//converts the decimal to a letter
str1<<(char)DeC;
//adds a space
str1<<" ";
//increases the decimal
DeC++;
}
else
{
num++;
DeC++;
}
}
//builds the sstream and puts it in
//variable "outted"
outted = str1.str();
return outted;
}
If you need to loop back to 'A' at Z change your DeC++ to
if DecC == 'Z'
DecC = 'A'
else
DecC++;
Or you could get fancy and use the modulus operator
Edit
I think the problem may be that this stringstream insertion operator, >>, doesn't have an overload that handles a char. It's converting the char to a short or an int then inserting it. Try using string::append(size_t size, char c) instead. That should handle inserting a char.
That is replace you calls to str1<<(char)DeC; with outted.append(1, (char)DeC) and remove your use of the string stream
What is DeC? The phrase "ascii list" makes me suspect it's a 'C' string, in which case you are calling isAlpha() on the pointer not on the value in the string.
edit: If for example you have
char DeC[40];
// read in a string form somewhere
// DeC is a pointer to some memory it has a value of a 32 or 64bit number
if ( isAlpha(DeC) {
// what you might have meant is
if ( isAlpha(*DeC) { // the character value at the current position in DeC