Stop a for loop when user is finished entering inp

2020-04-13 05:16发布

问题:

First of all, thank you for the assist!

I'm new to the C language (and programming in general) and I'm trying to write a program wherein the user inputs data points. The data points are then saved in an array where they can then be manipulated.

Where I am stuck: I want the user to be able to input (almost) any number of points, then use a 'keyword' of sorts to signal the end of data entry. In this case, the user would type 'done'.

Here's what I have so far:

#include <stdio.h>
#include <string.h>

int main(void) {

printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n");

double data[1048];
int i, count;

for (i = 1; ;i++) {

    printf("Data[%i]: ", i);
    scanf("%lf", &data[i]);

    if (data[i] == 'done') {

        break;

    } else {

        count++;

    }

  } 

}

I've tried 'return 1;' and 'break;'. Each time, the program works well until the 'keyword' is entered, at which point I get:

Data[8]: Data[9]: ... Data[1120]: Data[1Segmentation fault 11 

The only time it works is if I have it break when the user inputs a particular number (like -1 or 0). But that doesn't quite work for the user since they might have to enter those numbers as data points.

Sorry for the long post, but I appreciate the help!

回答1:

You have received a number of good answers to your question, and there are several more ways to take input of doubles and stop on "done". Since you are learning C, always, ALWAYS (in case it wasn't clear), check the return of scanf to validate the number of conversions you expected actually took place.[1] (this also provides your way to end input on "done" (or any non-double entered causing scanf to return less than 1)

As noted in the comment, arrays are zero based in C. When you are taking input, you will want to use count as your array-index, rather than i (in this case if you exit the read on each failure -- it doesn't matter, but you could just as easily prompt again for additional input and increment count only on a successful return from scanf) Back to your question. If you set up your read loop to continually loop until there is a scanf failure, you can make use of a temporary variable to initially capture the input value, and only assign the value to your array and increment your index on success. e.g. (with a constant MAXD = 1048)

for (;;) {  /* loop until scanf input fails (with 'done') */

    double tmp;  /* block scope declarations are fine */

    printf (" data[%4d]: ", count);

    if (count < MAXD && scanf(" %lf", &tmp) == 1)
        data[count++] = tmp;
    else
        break;
} 

(you can even move a copy of the prompt above the loop, and move the one above after the if (....) {...} to eliminate the prompt when the array limit (MAXD) is reached -- that's left as an exercise)

In the example above you have 2 conditions you enforce before storing a value. (1) you limit the number of values your user can store to MAXD, and (2) you only store a value if a valid conversion to double takes place in scanf. You leave the loop if either of the conditions fails (which if you enter "done" as a double-value, it will).

Putting the pieces together and dropping a few additional tips in the comments, you could test with something like the following:

#include <stdio.h>

enum { MAXD = 1048 };   /* declare constants instead of using magic numbers */

int main (void) {

    double data[MAXD] = {0};    /* in ISO C declarations come before code */
    int i, count = 0;           /* initializing variable saves debug time */

    printf ("\n Welcome! \n\n Please enter each data point. "
            "Enter 'done' when finished.\n\n");

    for (;;) {  /* loop until scanf input fails (with 'done') */

        double tmp;  /* block scope declarations are fine */

        printf (" data[%4d]: ", count);

        if (count < MAXD && scanf(" %lf", &tmp) == 1)
            data[count++] = tmp;
        else
            break;
    } 

    printf ("\n %d values entered:\n\n", count);

    for (i = 0; i < count; i++)
        printf ("  data[%4d] : %.2lf\n", i, data[i]);

    return 0;   /* main() is type 'int' and returns a value */
}

Example Use/Output

$ ./bin/scanfdoubles

 Welcome!

 Please enter each data point. Enter 'done' when finished.

 data[   0]: 1.1
 data[   1]: 1.2
 data[   2]: 1.3
 data[   3]: 1.4
 data[   4]: 1.5
 data[   5]: 1.6
 data[   6]: done

 6 values entered:

  data[   0] : 1.10
  data[   1] : 1.20
  data[   2] : 1.30
  data[   3] : 1.40
  data[   4] : 1.50
  data[   5] : 1.60

Look things over and let me know if you have any questions.

footnotes:

1. while you can use scanf to take user-input in C, you are better off using a line-oriented function (like fgets) and then parsing the complete line (with, e.g. sscanf). The allows you to both (1) validate the read (e.g. the return of fgets) and then (2) separately validate the value entered by the user. This decoupling of your read, and your parsing has many advantages.



回答2:

No element of data[] will ever be 'done' (they're floats). If you want to scanf() directly, you'll need to choose a double value that ends the sequence (commonly zero or -1 or something). If that won't work, you can either use something like:

  1. Use fgets() to pull a string, then strncmp() to check for the terminating value and sscanf() to pull out the double, or:
  2. Have the user use Ctrl-D to terminate and check the scan value for EOF.

Oh, and strictly speaking you have an upper limit of entries. You should check i to make sure that you don't exceed that. Never assume your input won't exceed boundaries. sizeof() on a statically-allocated variable or some #defined macro to track that.



回答3:

  • Your data is of type double. It can't scan a literal "done".

Instead use EOF for checking end of input.

while(scanf("%lf",&data[i]) != EOF) {
    ...
}

Another way:

while(scanf("%lf",&data[i]) == 1) {
    ...
}
  • Another thing, initialize count to zero, i.e. count = 0;


回答4:

Bottom line: don't use scanf.

Use something like

char inputline[100];

i = 0;

while(fgets(inputline, sizeof(inputline), stdin) != NULL) {
    if(strncmp(inputline, "done", 4) == 0) break;
    data[i++] = atof(inputline);
}

scanf is hard enough to use even when all your inputs are the numbers you expect. If the input might be either a number or the word "done", scanf will never work. But reading a line of text, as here, is generally easier and more flexible.

P.S. You also have to worry about the possibility that the user enters more than 1048 numbers.



回答5:

For your task the loop of gathering input should control not only keyword, but also number if inputs. I suggest to do this as follows:

#include <stdio.h>
#include <string.h>
#define NUM_OF_DATA 1048

int main(void) 
{
    printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n");
    double data[NUM_OF_DATA];
    int i; // counter of entered numbers
    char str[5] = { 0 }; // string to read 'done' or other word from input
    for (i = 0; i < NUM_OF_DATA; i++) // the first index of data in array is 0 (the last NUM_OF_DATA-1)
    {
        printf("Data[%i]: ", i);
        if (1 == scanf("%lf", &data[i])) // if number was successfully read
            continue; // go to next iteration
        // if some problem was with reading a loat number
        // read the string
        scanf("%4s", str); // read not more than 4 characters from input
        if ( strcmp(str, "done") == 0) 
        {
            break; // stop input if 'done' was entered
        }
        // clean input buffer before next input
        while (getchar() != '\n');
        // correct counter in case of wrong input
        i--;
    }
    // output the number of correct inputs
    printf("%d numbers were entered.\n", i);
    // do something with data
    // taking in account, that i is not index of the last element, 
    // but the number of elements (indexes are 0 ... i-1)
    // ...
    return 0;
}

This for loop stops in two cases:

1) when data array is full,

2) when 'done' without quotes entered.

Additional feature is skipping of incorrect input (try how it works).