tags:

views:

250

answers:

3

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?

+1  A: 

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;
MattSmith
Giving you the credit since you mentioned fgets and sscanf first.
Matt
Thanks, just realised c probably won't let you declare the buffer inside the while loop but the concept is there.
MattSmith
@MattSmith: That declaration is perfectly fine.
jamesdlin
+5  A: 

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> _
paxdiablo
+1 Using `scanf` to read input from the user often introduces problems like this. It is usually much less error-prone to simply read the input as a string and then parse the string.
bta
A: 

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.

Chris Dodd