Get scanf to quit when it reads a newline? - c

If I input 5 5 at the terminal, press enter, and press enter again, I want to exit out of the loop.
int readCoefficents(double complex *c){
int i = 0;
double real;
double img;
while(scanf("%f %f", &real, &img) == 2)
c[i++] = real + img * I;
c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
return i;
}
Obviously, that code isn't doing the job for me (and yes, I know there is a buffer overflow waiting to happen).
scanf won't quit unless I type in a letter (or some non-numeric, not whitespace string). How do I get scanf to quit after reading an empty line?

Use fgets to read console input:
int res = 2;
while (res == 2) {
char buf[100];
fgets(buf, sizeof(buf), stdin);
res = sscanf(buf, "%f %f", &real, &img);
if (res == 2)
c[i++] = real + img * I;
}
c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
return i;

The specific problem you're having is that a scanf format string of %f will skip white space (including newlines) until it finds an actual character to scan. From the c99 standard:
A conversion specification is executed in the following steps:
- Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a '[', 'c', or 'n' specifier.
and, elsewhere, describing isspace():
The standard white-space characters are the following: space ' ', form feed '\f', new-line '\n', carriage return '\r', horizontal tab '\t', and vertical tab '\v'.
Your best bet is to use fgets to get the line (and this can be protected from buffer overflow very easily), then use sscanf on the resultant line.
The scanf function is one of those ones you should look at very warily. The following piece of code is one I often use to handle line input:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Testing it with various combinations:
pax> ./prog
Enter string>[CTRL-D]
No input
pax> ./prog
Enter string> a
OK [a]
pax> ./prog
Enter string> hello
OK [hello]
pax> ./prog
Enter string> hello there
Input too long [hello the]
pax> ./prog
Enter string> i am pax
OK [i am pax]
What I would do is to use this function to get a line safely, then simply use:
sscanf (buffer, "%f %f", &real, &img)
to get the actual values (and check the count).
In fact, here's a complete program which is closer to what you want:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
int main (void) {
int i = 1, rc;
char prompt[50], buff[50];
float real, imag;
while (1) {
sprintf (prompt, "\nEnter real and imaginary for #%3d: ", i);
rc = getLine (prompt, buff, sizeof(buff));
if (rc == NO_INPUT) break;
if (*buff == '\0') break;
if (rc == TOO_LONG) {
printf ("** Input too long [%s]...\n", buff);
}
if (sscanf (buff, "%f %f", &real, &imag) == 2) {
printf ("Values were %f and %f\n", real, imag);
i++;
} else {
printf ("** Invalid input [%s]\n", buff);
}
}
return 0;
}
along with a test run:
pax> ./testprog
Enter real and imaginary for # 1: hello
** Invalid input [hello]
Enter real and imaginary for # 1: hello there
** Invalid input [hello there]
Enter real and imaginary for # 1: 1
** Invalid input [1]
Enter real and imaginary for # 1: 1.23 4.56
Values were 1.230000 and 4.560000
Enter real and imaginary for # 2:
pax> _

There's a way to do what you want using just scanf:
int readCoefficents(double complex *c) {
int i = 0;
double real;
double img;
char buf[2];
while (scanf("%1[\n]", buf) == 0) { // loop until a blank line or EOF
if (scanf("%lf %lf", &real, &img) == 2) // read two floats
c[i++] = real + img * I;
scanf("%*[^\n]"); // skip the rest of the line
scanf("%*1[\n]"); // and the newline
}
c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
return i;
}
If the user only enters 1 float on a line, it will read the next line for the second value. If any random garbage is entered, it will skip up to a newline and try again with the next line. Otherwise, it will just go on reading pairs of float values until the user enters a blank line or an EOF is reached.

re PAXDIABLO solution: it does not work properly with EMPTY line entered by user, so this line shall be added in your getLine() function
if (strlen(buff) <= 1) return NO_INPUT;
after the line:
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
So it will become :
...
if (strlen(buff) <= 1) return NO_INPUT;
if (fgets (buff, sz, stdin) == NULL) return NO_INPUT;
....

Instead of
while(scanf("%f %f", &real, &img) == 2)
try
while(scanf("%f %f%*c", &real, &img) == 2)
scanf("%f%*c", &myfloat); // will read a float and all eventual characters after it

Related

Read data to Text File and reserve de output with N character

I want read data from console and output to Text file with reserve of N character per variable of structure type.
The Text file is similar to:
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
I do not know if I'm using the most correct functions.
Also I can not read ("carro.name") more than 1 word (example: John Lays)
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
...
//Read data to Text File:
...
printf("\n ID......:"); scanf("%d", &carro.id);
printf("\n Potencia:"); scanf("%d", &carro.potencia);
printf("\n Avariado:"); scanf("%d", &carro.avariado);
printf("\n NAME:"); scanf("%10[0-9a-zA-Z ]", carro.name); // or scanf("%[^\n]s",...)
fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
...
//Show File Text data:
...
int registos=0;
while(1)
{
fscanf(fp, "%d %d %d %-10s", &carro.id, &carro.potencia, &carro.avariado, carro.name);
if(feof(fp)){ break; }
printf("%-2d %-3d %-1d %-10s\n", carro.id, carro.potencia, carro.avariado, carro.name);
registos++;
}
printf("\nCarros=%d", registos);
As you say in your question you cannot use scanf to read a complex name including spaces.
But before to search how to do it is needed to decide what to do.
Probably you do not want to memorize the extra spaces at the beginning and at the end (including the newline), and probably a name must not be empty.
But what about inside a complex name ? If the user enter John Lays do you save the name with the two spaces or you want to simplify to have only one ? Do you have to manage other special character like '-' (are John - Lays / John- Lays / John -Lays read as John-Lays ?).
What to do if the input string is longer than 10 characters ? Just to stop to read letting the rest for the next read or to bypass up to a newline ? Because you print a message before each input you clearly want an input per line and the rest of the line must be bypassed.
If you do not want to read the string as it is enter the best way is probably to write your own read string function.
You also have to decide what to do if the user do not enter a number for ID or Potencia or Avariado, currently you do not even detect the error, this is not a good way. So in that case do you abort all (exit program), or you redo the read ? Probably you prefer to read again, for that you need to bypass the invalid input, but what that means, to bypass all up to a newline ?
For instance :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* read an int memorizing its value in v,
return 0 in case of EOF else a non null value */
int readInt(const char * msg, int * v)
{
for (;;) {
fputs(msg, stdout);
if (scanf("%d", v) == 1)
return 1;
/* not a number or EOF, bypass all up to \n */
int c;
while ((c = fgetc(stdin)) != '\n')
if (c == EOF)
return 0;
puts("invalid value"); /* message may be also get in argument */
}
}
/* read a string up to a \n
remove extra spaces at the beginning and end
simplify internal multiple spaces
accept any character and do not manage in a special way characters like like '-'
a non empty string must be read
read at most sz-1 characters in s then place the null character (as fgets), sz must be > 1
if the line too long bypass the rest of the input up to \n
return 0 in case of EOF else a non null value */
int readStr(const char * msg, char * s, size_t sz)
{
fputs(msg, stdout);
/* read the first char bypassing spaces including \n */
if (scanf(" %c", s) == 0)
// EOF
return 0;
size_t index = 1;
int c;
sz -= 1;
while (index != sz) {
c = fgetc(stdin);
if ((c == EOF) || (c == '\n'))
break;
if (!isspace(c))
s[index++] = c;
else if (s[index - 1] != ' ')
s[index++] = ' ';
}
s[(s[index - 1] != ' ') ? index : index-1] = 0;
// bypass possible rest of the line
while ((c != EOF) && (c != '\n'))
c = fgetc(stdin);
return 1;
}
/* ******************* */
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
int main()
{
do {
if (!readInt("\n ID......:", &carro.id) ||
!readInt("\n Potencia:", &carro.potencia) ||
!readInt("\n Avariado:", &carro.avariado) ||
!readStr("\n NAME:", carro.name, sizeof(carro.name))) {
puts("EOF");
return -1;
}
else
printf("%-2d %-3d %-1d '%-10s' \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
} while (strcmp(carro.name, "end"));
return 0;
}
Compilation and execution:
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall r.c
pi#raspberrypi:/tmp $ ./a.out
ID......:aze
invalid value
ID......:qsd
invalid value
ID......:1
Potencia:2
Avariado:3
NAME:aze u iiiiiiiiiiiiiiiiii
1 2 3 'aze u iiii'
ID......:11
Potencia:22
Avariado:0
NAME: end
11 22 0 'end '
pi#raspberrypi:/tmp $
When you read in your file and supposing it was produced doing fprintf(fp, "%-2d %-3d %-1d %-10s", ...) :
char line[21]; /* each line has 20 characters newline included */
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%d %d %d", &carro.id, &carro.potencia, &carro.avariado) != 3)
/* abnormal case, invalid file */
break; /* anything else you want to do */
/* the string starts at the index 9 and has 10 characters out of the newline */
memcpy(carro.name, line + 9, 10);
carro.name[10] = 0;
/* ... */
}
note the name have spaces at the end if its length is less than 10 characters
Or you can read in a way similar to the previous on stdin.

How to check if user enters blank line in scanf in c

I need the program to exit the while loop if the user presses enter without entering any float value. Thanks!
printf("Enter scores\n”);
float scores[10];
int n=0;
while (n<10){
scanf("%f", &scores[n]);
n++;
if (THE USER PRESSES ENTER WITHOUT ENTERING ANYTHING){break;}
}
You can use fgets() to read a line of input through the newline character into a buffer, and then use sscanf() to parse the contents of the buffer. The problem with using scanf() for this is that most conversion specifiers, and in particular the %f conversion specifier, skip leading whitespace, including newlines. So, if you try to give an empty line to scanf(), the function will continue to wait for input until you enter a non-white-space character.
The code below adapts this technique to your code. The variable n has been changed to a size_t type variable, as this is an unsigned type guaranteed to be able to hold any array index. Furthermore, note that both fgets() and sscanf() return values that should be checked. The fgets() function returns a null pointer if there is an error, and the code below prints an error message and exits if this occurs. The sscanf() function returns the number of successful conversions made, and this value can be used to make sure that the input is as expected. When the user enters a blank line, or a line with no leading floating point value (leading white-space is OK), zero is returned, and the input loop is escaped.
I added some code to display the values entered into the array.
#include <stdio.h>
#include <stdlib.h> // for exit()
int main(void)
{
float scores[10];
char buffer[100];
size_t n = 0;
printf("Enter scores\n");
while (n < 10){
if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
exit(EXIT_FAILURE);
}
if (sscanf(buffer, "%f", &scores[n]) == 1) {
++n;
} else {
break;
}
}
for (size_t i = 0; i < n; i++) {
printf("scores[%zu] = %f\n", i, scores[i]);
}
return 0;
}
Sample interaction:
Enter scores
3.8
3.6
2.9
3.4
scores[0] = 3.800000
scores[1] = 3.600000
scores[2] = 2.900000
scores[3] = 3.400000
Separate input of user text from parsing.
Read the line of user input as a string;
char buffer[80];
if (fgets(buffer, sizeof buffer, stdin) == NULL) Handle_EOF();
Now, parse the string using sscanf(), strtod(), etc.
if (sscanf(buffer, "%f", &scores[n]) == 1) Handle_Successful_Float_Input(scores[n]);
else if (buffer[0] == '\n') Handle_Empty_Line();
else Handle_Everything_Else(buffer);
fgets is the better route but if scanf must be used, newline can be detected by scanning a character. If the character is not a newline, replace it in the input with ungetc and then scan the float.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE 10
int main ( void) {
char i = '\0';
int n = 0;
int result = 0;
float scores[SIZE] = { 0.0f};
printf ( "enter scores\n");
while ( n < SIZE && ( scanf("%c",&i) == 1)) {//scan a character
if ( i == '\n') {
break;//newline so leave loop
}
else {
ungetc ( i, stdin);//replace the character in input stream
}
if ( ( result = scanf ( "%f", &scores[n])) == 1) {//scan a float
printf ( " score[%d] entered as %f\n", n, scores[n]);
n++;
}
if ( result == 0) {
printf ( "please enter a number\n");//could not scan the input as a float
}
while ( ( result = getchar ( )) != '\n') {//clean the input stream
if ( result == EOF) {
fprintf ( stderr, "problem EOF\n");
return 1;
}
}
printf ( "enter score[%d]\n", n);
}
return 0;
}
I thought . you want to check that if an integer is assigned a value or not ?
Actually you have to initialize the variable otherwise it will contain whatever happen at that memory location unless it is declared as global. If you are not getting then check
this might help you with your question
We know that scanf returns a negative value if nothing of the defined type is fetched from the input.
So:
printf("Enter scores\n”);
float scores[10];
int n=0;
while (n<10){
if(scanf("%f\n", &scores[n]) < 0) printf("Error! Error!");
n++;
}
See http://www.cplusplus.com/reference/cstdio/scanf/

proper use of scanf in a while loop to validate input

I made this code:
/*here is the main function*/
int x , y=0, returned_value;
int *p = &x;
while (y<5){
printf("Please Insert X value\n");
returned_value = scanf ("%d" , p);
validate_input(returned_value, p);
y++;
}
the function:
void validate_input(int returned_value, int *p){
getchar();
while (returned_value!=1){
printf("invalid input, Insert Integers Only\n");
getchar();
returned_value = scanf("%d", p);
}
}
Although it is generally working very well but when I insert for example "1f1" , it accepts the "1" and does not report any error and when insert "f1f1f" it reads it twice and ruins the second read/scan and so on (i.e. first read print out "invalid input, Insert Integers Only" and instead for waiting again to re-read first read from the user, it continues to the second read and prints out again "invalid input, Insert Integers Only" again...
It needs a final touch and I read many answers but could not find it.
If you don't want to accept 1f1 as valid input then scanf is the wrong function to use as scanf returns as soon as it finds a match.
Instead read the whole line and then check if it only contains digits. After that you can call scanf
Something like:
#include <stdio.h>
int validateLine(char* line)
{
int ret=0;
// Allow negative numbers
if (*line && *line == '-') line++;
// Check that remaining chars are digits
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1; // Remember that at least one legal digit was found
++line;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin)) // Read the whole line
{
if (validateLine(line)) // Check that the line is a valid number
{
// Now it should be safe to call scanf - it shouldn't fail
// but check the return value in any case
if (1 != sscanf(line, "%d", &x))
{
printf("should never happen");
exit(1);
}
// Legal number found - break out of the "while (y<5)" loop
break;
}
else
{
printf("Illegal input %s", line);
}
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Input
1f1
f1f1
-3
Output
Please Insert X value
Illegal input 1f1
Please Insert X value
Illegal input f1f1
Please Insert X value
Illegal input
Please Insert X value
x=-3
Another approach - avoid scanf
You could let your function calculate the number and thereby bypass scanf completely. It could look like:
#include <stdio.h>
int line2Int(char* line, int* x)
{
int negative = 0;
int ret=0;
int temp = 0;
if (*line && *line == '-')
{
line++;
negative = 1;
}
else if (*line && *line == '+') // If a + is to be accepted
line++; // If a + is to be accepted
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1;
// Update the number
temp = 10 * temp;
temp = temp + (*line - '0');
++line;
}
if (ret)
{
if (negative) temp = -temp;
*x = temp;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin))
{
if (line2Int(line, &x)) break; // Legal number - break out
printf("Illegal input %s", line);
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Generally speaking, it is my opinion that you are better to read everything from the input (within the range of your buffer size, of course), and then validate the input is indeed the correct format.
In your case, you are seeing errors using a string like f1f1f because you are not reading in the entire STDIN buffer. As such, when you go to call scanf(...) again, there is still data inside of STDIN, so that is read in first instead of prompting the user to enter some more input. To read all of STDIN, you should do something the following (part of code borrowed from Paxdiablo's answer here: https://stackoverflow.com/a/4023921/2694511):
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // Used for strtol
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define NaN 3 // Not a Number (NaN)
int strIsInt(const char *ptrStr){
// Check if the string starts with a positive or negative sign
if(*ptrStr == '+' || *ptrStr == '-'){
// First character is a sign. Advance pointer position
ptrStr++;
}
// Now make sure the string (or the character after a positive/negative sign) is not null
if(*ptrStr == NULL){
return NaN;
}
while(*ptrStr != NULL){
// Check if the current character is a digit
// isdigit() returns zero for non-digit characters
if(isdigit( *ptrStr ) == 0){
// Not a digit
return NaN;
} // else, we'll increment the pointer and check the next character
ptrStr++;
}
// If we have made it this far, then we know that every character inside of the string is indeed a digit
// As such, we can go ahead and return a success response here
// (A success response, in this case, is any value other than NaN)
return 0;
}
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
// (Per Chux suggestions in the comments, the "buff[0]" condition
// has been added here.)
if (buff[0] && buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
void validate_input(int responseCode, char *prompt, char *buffer, size_t bufferSize){
while( responseCode != OK ||
strIsInt( buffer ) == NaN )
{
printf("Invalid input.\nPlease enter integers only!\n");
fflush(stdout); /* It might be unnecessary to flush here because we'll flush STDOUT in the
getLine function anyway, but it is good practice to flush STDOUT when printing
important information. */
responseCode = getLine(prompt, buffer, bufferSize); // Read entire STDIN
}
// Finally, we know that the input is an integer
}
int main(int argc, char **argv){
char *prompt = "Please Insert X value\n";
int iResponseCode;
char cInputBuffer[100];
int x, y=0;
int *p = &x;
while(y < 5){
iResponseCode = getLine(prompt, cInputBuffer, sizeof(cInputBuffer)); // Read entire STDIN buffer
validate_input(iResponseCode, prompt, cInputBuffer, sizeof(cInputBuffer));
// Once validate_input finishes running, we should have a proper integer in our input buffer!
// Now we'll just convert it from a string to an integer, and store it in the P variable, as you
// were doing in your question.
sscanf(cInputBuffer, "%d", p);
y++;
}
}
Just as a disclaimer/note: I have not written in C for a very long time now, so I do apologize in advance if there are any error in this example. I also did not have an opportunity to compile and test this code before posting because I am in a rush right now.
If you're reading an input stream that you know is a text stream, but that you are not sure only consists of integers, then read strings.
Also, once you've read a string and want to see if it is an integer, use the standard library conversion routine strtol(). By doing this, you both get a confirmation that it was an integer and you get it converted for you into a long.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool convert_to_long(long *number, const char *string)
{
char *endptr;
*number = strtol(string, &endptr, 10);
/* endptr will point to the first position in the string that could
* not be converted. If this position holds the string terminator
* '\0' the conversion went well. An empty input string will also
* result in *endptr == '\0', so we have to check this too, and fail
* if this happens.
*/
if (string[0] != '\0' && *endptr == '\0')
return false; /* conversion succesful */
return true; /* problem in conversion */
}
int main(void)
{
char buffer[256];
const int max_tries = 5;
int tries = 0;
long number;
while (tries++ < max_tries) {
puts("Enter input:");
scanf("%s", buffer);
if (!convert_to_long(&number, buffer))
break; /* returns false on success */
printf("Invalid input. '%s' is not integer, %d tries left\n", buffer,
max_tries - tries);
}
if (tries > max_tries)
puts("No valid input found");
else
printf("Valid input: %ld\n", number);
return EXIT_SUCCESS;
}
ADDED NOTE: If you change the base (the last parameter to strtol()) from 10 to zero, you'll get the additional feature that your code converts hexadecimal numbers and octal numbers (strings starting with 0x and 00 respectively) into integers.
I took #4386427 idea and just added codes to cover what it missed (leading spaces and + sign), I tested it many times and it is working perfectly in all possible cases.
#include<stdio.h>
#include <ctype.h>
#include <stdlib.h>
int validate_line (char *line);
int main(){
char line[256];
int y=0;
long x;
while (y<5){
printf("Please Insert X Value\n");
if (fgets(line, sizeof(line), stdin)){//return 0 if not execute
if (validate_line(line)>0){ // check if the string contains only numbers
x =strtol(line, NULL, 10); // change the authentic string to long and assign it
printf("This is x %d" , x);
break;
}
else if (validate_line(line)==-1){printf("You Have Not Inserted Any Number!.... ");}
else {printf("Invalid Input, Insert Integers Only.... ");}
}
y++;
if (y==5){printf("NO MORE RETRIES\n\n");}
else{printf("%d Retries Left\n\n", (5-y));}
}
return 0;}
int validate_line (char *line){
int returned_value =-1;
/*first remove spaces from the entire string*/
char *p_new = line;
char *p_old = line;
while (*p_old != '\0'){// loop as long as has not reached the end of string
*p_new = *p_old; // assign the current value the *line is pointing at to p
if (*p_new != ' '){p_new++;} // check if it is not a space , if so , increment p
p_old++;// increment p_old in every loop
}
*p_new = '\0'; // add terminator
if (*line== '+' || *line== '-'){line++;} // check if the first char is (-) or (+) sign to point to next place
while (*line != '\n'){
if (!(isdigit(*line))) {return 0;} // Illegal char found , will return 0 and stop because isdigit() returns 0 if the it finds non-digit
else if (isdigit(*line)){line++; returned_value=2;}//check next place and increment returned_value for the final result and judgment next.
}
return returned_value; // it will return -1 if there is no input at all because while loop has not executed, will return >0 if successful, 0 if invalid input
}

Scanf keeps looping

Well i have this code to find the paint quality of a room.
void get_room_size(char room_id, int * length, int * width) {
while (*length <= 0 && *width <= 0) {
printf("Enter length and width of room %c in feet: ", room_id);
if (scanf("%d,%d", length, width)) {
if (*length <= 0) {
printf("###Error! Length must be a positive value!\n");
}
if (*width <= 0) {
printf("###Error! Width must be a positive value!\n");
}
printf("\n");
} else {
printf("bad data");
*length = 0;
*width = 0;
}
}
}
Basically, if i enter
a,1
It will go crazy and keep looping. Whats the problem?
The reason it's going "crazy" is as follows. When scanf fails to read in a as a number (because it's not numeric, obviously), it won't advance the file pointer.
This is one reason why you shouldn't generally use scanf operations, a failure can leave the file pointer in an indeterminate position (such as if you only scan in 3 of 12 items).
The other reason is that scanf means "scan formatted" and you would be hard pressed finding anything more unformatted than user input.
Anyway, back to the failure. Because the file pointer isn't advanced, the next time you come back to do fscanf, it will try to read that a again (and again and again).
If you want a decent function for handling user input, look no further than here:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
This will input a line from the user, with overflow protection (unlike gets or scanf with unbounded "%s").
It also flushes to end of line if the input was too long, which will stop the remainder of the line from affecting the next input operation.
You can then sscanf the buffer to your heart's content without any concerns re the file pointer.
The following test program shows how to use this:
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
As an aside, you may want to re-examine your logic for a valid sized room. What you currently have would allow a room to be entered as 7 by -42 feet :-)
It's also not usually good form to rely on the output values being set to specific values on entry. If length and width are (for example) 3 and 4 on entry, this function will exit straight away without asking the user for input.
The first problem can be fixed by using || instead of &&. The second by initialising the variables to 0 at the start of the function so that the loop is entered.
For completeness, if you combine that original snippet above (the include statements and the getLine() function) with the following slightly modified get_room_size() function:
static void get_room_size(char room_id, int * length, int * width) {
char buff[100];
*length = *width = 0;
while ((*length <= 0) || (*width <= 0)) {
printf("Enter length and width of room %c in feet: ", room_id);
int rc = getLine (NULL, buff, sizeof (buff));
if (rc == NO_INPUT) {
printf ("\nEnd of file encountered.\n");
return;
}
if (rc == TOO_LONG) {
printf ("\nInput too long, please retry.\n");
continue;
}
if (sscanf(buff, "%d,%d", length, width) != 2) {
*length = *width = 0;
printf ("\nInput not in desired form (<number>,<number>), "
"please retry.\n");
continue;
}
if ((*length <=0) || (*width <= 0)) {
*length = *width = 0;
printf ("\nBoth length and width must be greater than zero, "
"please retry.\n");
}
}
}
and the very simple test main(), you'll see a complete program showing how to do it.
int main (void) {
int len, wid;
get_room_size ('x', &len, &wid);
printf ("Length is %d, width is %d.\n", len, wid);
return 0;
}
your scanf is taking in two integer. A is a character, think you want to
scanf("%c", *room_id);
scanf("%d", length);
I reccommend you do them separately
Try this:
...
if (scanf("%d,%d", length, width) == 2) {
if (*length <= 0) {
printf("###Error! Length must be a positive value!\n");
}
if (*width <= 0) {
printf("###Error! Width must be a positive value!\n");
}
printf("\n");
} else {
printf("bad data");
*length = -1;
*width = -1;
}
You dont put & in scanf() statement so how its read
if (scanf("%d,%d", &length, &width))

Is this vulnerable to a stack overflow?

void gctinp (char *inp, int siz)
{
puts ("Input value: ");
fgets (inp, siz, stdin);
printf ("buffer3 getinp read %s", inp);
}
From what I've read, fgets is supposed to be used when you want to limit the size of input. So this code shouldn't be vulnerable right?
It is being called like so:
int main (int argc, char *argv[])
{
char buf[16];
getinp (buf, sizeof (buf));
display (buf);
printf ("buffer3 done\n");
}
Thanks for your time.
You won't strike buffer overflow problems if you enter more characters than can be safely stored since fgets restricts the input. It also adds a null terminator (assuming buffer size is greater than 0, of course).
However, you will have problems with information being left in the input buffer the next time you try to read something - this is something that users will find very annoying, entering something like hello again and having it treated as two separate inputs like hello ag and ain. And there's no indication given by fgets that it stopped retrieving input before the end of the line so, as far as your code is aware, everything is fine.
The major things you need to look out for (re buffer overflows on input) are, at a minimum, scanf with an unbounded %s format string and gets, which has no limiting size argument, neither of which are in your code.
If you're looking for a more robust input solution with size limiting, prompting and buffer clearing, check out this code, which provides all those features:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
rc = getLine ("Hit ENTER to check remains> ", buff, sizeof(buff));
printf ("Excess [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
And, doing some basic tests:
pax> ./prog
Enter string> [CTRL-D]
No input
pax> ./prog
Enter string> x
OK [x]
pax> ./prog
Enter string> hello
OK [hello]
pax> ./prog
Enter string> hello from earth
Input too long [hello fro]
Hit ENTER to check remains> [ENTER]
Excess []
pax> ./prog
Enter string> i am pax
OK [i am pax]
No, it isn't prone to stack overflow.
Are you confusing stack overflow and buffer overflow by any chance?
http://en.wikipedia.org/wiki/Stack_overflow
fgets will read at most one less than the specified number of bytes, and will make sure that the read string is null-terminated. So as long as you pass the correct size, it should be fine (although the string might not end in a newline).

Resources