简体   繁体   中英

Space at the end of input line causes while loop to go one more time than it should

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    int a = 0, f = 0;
    char ch[1];
    char arr[1000];
    while (f < 3) {
        while (((*ch = getchar()) != 10))
        {
            scanf("%9s", &(arr[a]));
            scanf("%9s", &(arr[a + 1]));
            a += 2;
        }
        f++;
    }

    for (int i = 0; i < a; i++) {
        printf("%s\n", &arr[i]);
    }

    return 0;
}

This code is supposed to collect input like:

1 4 green 4 green 1 blue 2 blue 4 blue 5 blue 7 blue  5 red 7 red 1 violet 2 violet 4 violet 5 violet 7 violet 

1 

2 4 green 4 green  7 blue 1 red 2 red 4 red 5 red 7 red 1 violet 2 violet 4 violet 5 violet 7 violet

2 

3 4 green 4 green 1 blue 2 blue 4 blue  7 blue 1 red 2 red 4 red 5 red 7 red  5 violet 7 violet

3 

First, there is some number, then one number and one word. I don't know how many times will there be but I know the number of input lines. The problem I encountered is when there is a space at the end of input line right before pressing 'enter' in console, program keeps on waiting for more input. I have no idea how to get rid of this especially at the last line of input because putting space at the end of last input line causes program to wait forever for a user to type something.

Let's unpack your code.

You're using getchar to look for an end of line, but it will swallow that character. scanf cannot see it. If you input "0123456789" getchar will eat the 0. You could use putchar to put it back, but it's all unnecessary.

    scanf("%9s", &(arr[a]));
    scanf("%9s", &(arr[a + 1]));

This is reading up to nine characters from input and sticking it into the string arr . Then it's reading another nine characters from input and overwriting eight of the characters it just read.

To walk through it, let's say the line is "0123456789abcdefghijklmno"

  • a: 0
  • stdin: "0123456789abcdefghijklmno\n"
  • arr: "garbagegarbagegarbagegarbage" // arr is uninitialized

getchar() reads 0 from stdin.

  • a: 0
  • stdin: "123456789abcdefghijklmno\n"
  • arr: "garbagegarbagegarbagegarbage"

scanf("%9s", &(arr[a])) reads 123456789 from stdin and puts it in arr starting at a which is 0.

  • a: 0
  • stdin: "abcdefghijklmno\n"
  • arr: "123456789\0rbagegarbagegarbage"

scanf("%9s", &(arr[a + 1])) reads abcdefghi from stdin and puts it in arr starting at a + 1 which is 1 overwriting all but the first character of what was just written to arr .

  • a: 0
  • stdin: "jklmno\n"
  • arr: "1abcdefghi\0bagegarbagegarbage"

arr[a] will be "1abcdefghi".


char arr[1000] allocates space for a single string of 999 characters (plus a null byte) to the stack. I think you mean arr to be an array of 1000 strings, each able to hold at least 9 characters plus the null byte. That's char arr[1000][10] .

scanf has many problems , as does trying to read line-by-line with getchar . Instead, it's usually better to read a line with fgets into a buffer and parse the line with sscanf or other string parsing functions. This avoids many issues with hanging waiting for input or unknown line size.

In this case, use strtok (STRing TOKenizer) to split the line on whitespace. strtok behaves a little oddly because it has its own internal buffer. It also does not allocate new memory, it returns pointers within the original line. The spaces are replaced with null bytes so it seems like you're getting just one token at a time. Use strdup to copy the word into your array.

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

int main(void)
{
    // A single line buffer to reuse.
    // BUFSIZ is the size of the input buffer and a good size to choose.
    char line[BUFSIZ];

    // An array for 1000 strings.
    // No memory is allocated for the strings themselves.
    // We'll allocate as needed.
    //
    // Note: a real problem would not allocate a fixed size array.
    // It would extend `words` as needed.
    // You'll learn how to do that that later.
    char *words[1000];

    // Track how many words are in the words array.
    int num_words = 0;

    // Read at most 3 lines
    for(int i = 0; i < 3; i++) {
        // Read a line. Exit the loop early if there's no more lines.
        if( !fgets(line, sizeof(line), stdin) ) {
            break;
        }

        // Split the line on whitespace.
        // fgets() does not strip newlines.
        // We must include newline else it will be considered a word.
        for(
            char *word = strtok(line, " \t\n");
            word;
            word = strtok(NULL, " \t\n")
        ) {
            // word is a pointer to memory inside line.
            // The whitespace has been replaced by a null byte so we can
            // read a single word. We could store that and save memory,
            // but `line` will be reused to read the next line and
            // the previous words will be overwritten.
            // So we need to copy the word. `strdup` allocates just the
            // right amount of memory.
            words[num_words] = strdup(word);
            num_words++;
        }
    }

    for (int i = 0; i < num_words; i++) {
        printf("%s\n", words[i]);
    }

    return 0;
}

You should be able to adapt that basic code to read lines of words to your specific task.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM