首页 > 解决方案 > 输入行末尾的空格导致while循环比它应该多走一次

问题描述

#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;
}

此代码应该收集如下输入:

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 

首先,有一些数字,然后是一个数字和一个单词。我不知道会有多少次,但我知道输入行的数量。我遇到的问题是,在控制台中按“enter”之前,输入行末尾有一个空格,程序一直在等待更多输入。我不知道如何摆脱这一点,尤其是在输入的最后一行,因为在最后一行的末尾放置空格会导致程序永远等待用户输入某些内容。

标签: cscanf

解决方案


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.


推荐阅读