C принять входные данные о переходных отношениях - PullRequest
0 голосов
/ 29 октября 2018

Как мне принять ввод, как это:

a<b
b<c

а также вот так:

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

В настоящее время я могу читать отдельные операторы вроде a Мой код для отдельных операторов был:

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. Так что некоторая помощь будет признательна.

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Считайте каждое выражение в строку, затем проанализируйте его, используя sscanf() и шаблон %n, чтобы получить длину проанализированной части. (Просто обратите внимание, что %n не учитывается как преобразование большинством библиотек Си.)

Например:

#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(), выше) будет вызываться один раз для каждой уникальной пары. Он принимает слова как имена, а не только буквы. Каждое «слово» может быть длиной до 31 символа и может содержать любые символы, кроме пробелов, <, , или ;.

Для полноты он допускает точки с запятой между подвыражениями и игнорирует их.

Сама логика проста.

Сначала parse() пытается отсканировать точку с запятой. Любые пробелы вокруг него также будут пропущены.

Затем он пытается отсканировать пару «слов» с < между ними. Если это не удается, он выходит из бесконечного цикла и возвращает указатель на первый неразобранный символ. Он будет указывать на байт NUL (\0), если синтаксис выражения был правильным.

Если пара была отсканирована, для нее вызывается функция обратного вызова (если только функция обратного вызова не равна NULL).

Затем внутренний цикл пытается отсканировать запятую, за которой следует «слово». Пока это успешно, для пары будет вызываться функция обратного вызова (используя исходную левую сторону и это новое «слово» в качестве правой стороны). В противном случае мы вырываемся из внутреннего цикла и начинаем новую итерацию внешнего цикла.

Итак, если мы посмотрим, скажем, foo < bar foo < baz, war war < bar, то foo < bar будет проанализирован первой итерацией внешнего цикла; foo < baz будет проанализирован второй итерацией внешнего цикла, а , war - внутренним циклом; и, наконец, war < bar будет проанализирован третьей итерацией самого внешнего цикла.

0 голосов
/ 29 октября 2018

Вы можете использовать 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;
}
...