C 接受关于传递关系的输入

C take input about transitive relations

如何接受这样的输入:

a<b
b<c

还有这样的:

a<b,c
d<e
f<x,y,z

目前我能够读取像 a < b 这样的单个语句,但不能像 a < b,c 这样读取一行中的多个语句。

我的单个语句代码是:

char a, b;
while(scanf("%c<%c\n", &a, &b) == 2)
    /* Set alpha[0..26][0..26] to 1 */
    alpha[a - 'a'][b - 'a'] = 1;

我对每行多个语句尝试了类似这样的方法:

char a;
while(scanf("%c<", &a)) {
    char b;
    while(scanf("%c,", &b)) {
        if(getchar() == '\n') break;
        /* Set alpha[0..26][0..26] to 1 */
        alpha[a - 'a'][b - 'a'] = 1;
    }
}

但它不符合我的要求。它卡在第一个 while 循环中。 我不太擅长 scanf 和读取 C 中的输入。所以一些帮助将不胜感激。

你可以用getchar来解决。

以下 code 可行:

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

int main(){
    int a;
    int b;
    char alpha[100][100];

    while ((a=getchar()) != EOF) {
        getchar(); // for <
        while((b=getchar()) != '\n') {
            /* Set alpha[0..26][0..26] to 1 */
            alpha[a - 'a'][b - 'a'] = 1;
            b = getchar();
            if (b == ',')
                continue;
            else
                break;
        }
    }
    return 0;
}

将每个表达式读入一个字符串,然后使用sscanf()%n 模式解析它以获得解析部分的长度。 (请注意,%n 不被大多数 C 库计为转换。)

例如:

#include <stdlib.h>
#include <stdio.h>
#define  STRINGIFY_(x) #x
#define  STRINGIFY(x)  STRINGIFY_(x)

#ifndef  MAX_WORD_LEN
#define  MAX_WORD_LEN  31
#endif
#define  SCAN_WORD "%" STRINGIFY(MAX_WORD_LEN) "[^\t\n\v\f\r <,;]"

const char *parse(const char *spec,
                  void      (*callback)(const char *less, const char *greater))
{
    char  less[MAX_WORD_LEN+1];
    char  more[MAX_WORD_LEN+1];
    int   len;

    while (1) {

        /* Skip an optional semicolon. */
        len = -1;
        if (sscanf(spec, " ; %n", &len) >= 0 && len > 0)
            spec += len;

        /* Try parsing the first pair. */
        len = -1;
        if (sscanf(spec, " " SCAN_WORD " < " SCAN_WORD " %n", less, more, &len) < 2 || len < 0)
            break;

        /* Report it. */
        if (callback)
            callback(less, more);

        /* Scan additional right sides. */
        spec += len;
        while (1) {

            len = -1;
            if (sscanf(spec, " , " SCAN_WORD " %n", more, &len) < 1 || len < 0)
                break; /* Only out of this inner while loop. */

            /* Report this one too. */
            if (callback)
                callback(less, more);

            spec += len;
        }
    }

    /* Return a pointer to the first unparsed character. */
    return spec;
}

将其应用于命令行中指定的每个表达式的示例程序:

void report(const char *left, const char *right)
{
    printf("    %s < %s\n", left, right);
}

int main(int argc, char *argv[])
{
    int         arg;
    const char *end;

    for (arg = 1; arg < argc; arg++) {
        printf("%s:\n", argv[arg]);

        end = parse(argv[arg], report);
        if (*end)
            printf("but with '%s' not parsed.\n", end);
        else
            printf("completely parsed.\n");
    }

    return EXIT_SUCCESS;
}

如果你把上面的编译成example,而你运行

./example 'foo < bar foo < baz, war war < bar'

程序会输出

foo < bar foo < baz, war war < bar:
    foo < bar
    foo < baz
    foo < war
    war < bar
completely parsed.

也就是说,回调函数(report(),上文)将针对每个唯一对调用一次。它接受 "words" 作为名称,而不仅仅是单个字母。每个 "word" 最多可以包含 31 个字符,并且可以包含除空格、<,;.

之外的任何字符

为了完整起见,它允许在子表达式之间使用分号,并忽略它们。

逻辑本身很简单。

首先,parse() 尝试扫描分号。它周围的任何空格也将被跳过。

接下来,它会尝试扫描一对 "words",中间有一个 <。如果失败,它会跳出无限循环,并且 returns 指向第一个未解析字符的指针。如果表达式的语法正确,它将指向 NUL 字节 ([=24=])。

如果扫描到一对,则为该对调用回调函数(除非回调函数为 NULL)。

接下来,内部循环尝试扫描逗号后跟 "word"。只要此操作成功,就会为该对调用回调函数(使用原来的左侧,而这个新的 "word" 作为右侧)。否则,我们跳出内循环,并开始新的外循环迭代。

所以,如果我们看foo < bar foo < baz, war war < bar,那么foo < bar将被外循环的第一次迭代解析; foo < baz会被外循环的第二次迭代解析,, war会被内循环解析;最后 war < bar 将被最外层循环的第三次迭代解析。