使用 scanf() 和 fgets() 读取由空格、括号和逗号分隔的输入

Read an input that is separated by spaces, parenthesis, and commas with scanf() and fgets()

我有以下输入:

1 (2 ,3 ,4) lantern

括号之间的int输入数量未知,可能会延长一段时间。

我最初的想法是scanf()第一个int,然后创建一个while循环来确定何时扫描封闭的paranethsis。然后最后用fgets()得到最后的字符串,类似这样。

scanf("%d", &address);  //first input

scanf("%c", &paren);    //scan the '(' or ',' or ')'

int current_room = 0;   //index for array inside parenthsis

while(paren == '(' || paren == ','){

    scanf("%d,", adjoined_room[current_room]);  //scan am int

    scanf("%c", &paren);   //scan a ',' or ')'

    current_room++;        //increase the index

}

当我打印地址、数组和字符串时,这会打印以下输出:

Address: 1
Item: (2 ,3 ,4) lantern

括号之间的输入整数从未设置为数组。是否有更好的方法来确定何时输入')'?

scanf 绝不能使用。曾经。但是......你可以尝试这样的事情:

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

void * xrealloc(void *buf, size_t num, size_t siz);

int
main(void)
{
        size_t cap = 4;
        char buf[1024];
        int *x = xrealloc(NULL, cap, sizeof *x);
        if( scanf("%d ( %d", x, x + 1) != 2 ){
                errx(EXIT_FAILURE, "Ivalid input");
        }
        int *y = x + 2;
        while( scanf(",%d", y) == 1 ){
                if( ++y == x + cap ){
                        cap += 4;
                        x = xrealloc(x, cap, sizeof *x);
                }
        }
        if( scanf(")%1023s", buf) != 1 ){
                errx(EXIT_FAILURE, "Ivalid input");
        }
        for( unsigned i = 0; i < y - x; i += 1 ){
                printf("x[%d] = %d\n", i, x[i]);
        }
        printf("%s\n", buf);
        return 0;
}
void *
xrealloc(void *buf, size_t num, size_t siz)
{
        char *b = buf;
        b = realloc(b, num * siz);
        if( b == NULL ){
                perror("realloc");
                exit(EXIT_FAILURE);
        }
        return b;
}

这不能正确处理带有尾随逗号的输入,例如:1 (2 ,3 ,4, ) lantern,而且我敢肯定还有许多它不喜欢的其他输入。练习留给 reader.

你可能不想使用小到 4 的初始容量,但它方便简单测试。

问题是 scanf("%c", 将读取输入中的下一个字符,而不会跳过任何白色 space。如果要跳过whitespace,则格式中需要一个space,例如scanf(" %c",。您还应该检查 scanf return 值以确保您得到一个整数

将其添加到您的代码中可以得到类似的东西:

if (scanf("%d", &address) != 1) {  //first input
    fprintf(stderr, "syntax error\n");
    return;  // not an integer -- do something else
}
scanf(" %c", &paren);    //scan the '(' or ',' or ')'
int current_room = 0;   //index for array inside parenthsis
while(paren == '(' || paren == ','){
    if (scanf("%d", adjoined_room[current_room]) == 1) {  //scan an int
        current_room++;        //increase the index
    }
    scanf(" %c", &paren);   //scan a ',' or ')'
    if (paren != ',' && paren != ')') {
        fprintf(stderr, "syntax error\m");
        return;
    }
}

如果你想用交互式输入来做到这一点,你可能应该使用 fgetsgetline 来读取整行,并使用 sscanf 来独立解析每一行,这样你就不会当一行中间有错误时,会让你的用户感到困惑。如果您想尝试多种不同的模式,“读取行 + sscanf”也非常有用(sscanf 在同一行上使用不同的格式找到第一个匹配的模式)。

这可能不是最受欢迎的答案,它可能有助于也可能不会帮助您实现近期目标,但我的理念是将输入读取为字节流并通过(粗略或复杂的)状态机进行解析:

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

#define process_word(x) (printf("Got string \'%s\'\n", x))
#define process_number(x) (printf("Got number %lu\n", strtoul(x, NULL, 10)))

int main(void) {
        int c;
        int depth = 0;
        size_t i;
        char digitbuffer[256];
        char alphabuffer[256];

        while ((c = fgetc(stdin)) != EOF) {
                switch (c) {
                case ' ': 
                case ',':
                        break;
                case '(': 
                        depth++;
                        break;
                case ')':
                        if (depth == 0) perror("Mismatched parenthesis, skipping");
                        else depth--;
                        break;
                default:
                        if (isalpha(c)) {
                                memset(alphabuffer, 0, 256);
                                alphabuffer[0] = c;
                                i = 1;
                                while ((c = fgetc(stdin)) != EOF &&
                                    isalpha(c) &&
                                    i < 255) {
                                        alphabuffer[i++] = c;
                                }
                                if (!isalpha(c) && c != EOF) ungetc(c, stdin);
                                process_word(alphabuffer);
                        }
                        else if (isdigit(c)) {
                                memset(digitbuffer, 0, 256);
                                digitbuffer[0] = c;
                                i = 1;
                                while ((c = fgetc(stdin)) != EOF &&
                                    isdigit(c) &&
                                    i < 255) {
                                        digitbuffer[i++] = c;
                                }
                                if (!isdigit(c) && c != EOF) ungetc(c, stdin);
                                process_number(digitbuffer);
                        }
                        break;
                }
        }

        return 0;
}

在我看来,这使您可以最大程度地控制处理特定数据格式。

当然,您可以定义自己的 process_word()process_number() 函数。例如,如果 depth == 0process_number() 可能会将数字分配给记录的 address 字段,或者如果 depth == 1,则将其添加到 adjacent_room[]process_word() 可能会将字符串添加到同一记录的 item 字段。完全取决于你。 ¯\_(ツ)_/¯