написание кода, который проверяет только восходящую последовательность, используя только циклы и состояния условий - PullRequest
0 голосов
/ 16 мая 2019

Меня попросили написать код на языке c, который проверяет определенный алфавитный порядок на входе и определяет, сколько существует «законных» порядков. порядок выглядит следующим образом: я получаю несколько входных данных как от цифр (от 1 до 9), так и от букв (от A-Z), за которыми следует '@', определяющий конец ввода. Как только число получено, я должен проверить следующие буквы (то есть, если я получил число 3, я должен проверить следующие 3 буквы и т. д.), эти буквы должны быть организованы в возрастающем алфавитном порядке. например, ABC3DEF @ (ABC- не является допустимой последовательностью. Однако 3DEF является допустимой последовательностью, поэтому в целом у меня есть одна допустимая последовательность.)

Я пытался что-то, но это не работает, вывод всегда 0! (примечание: мне разрешено использовать только циклы и операторы условия, то есть без массива, функций, указателей ...). Есть идея, что мне не хватает? или моя идея была неправильной?

int i, x, sum = 0;
char cha, c = 0;
while((cha = getchar()) != '@') {
    scanf("%d %c", &x, &cha);
    cha = c;
    if(x >= 1 && x <= 9) {
        for(i = 0; i < x; i++) {
            if(c == cha - i) {
                sum++;
            }
            c--;
        }
    }
}

Ответы [ 2 ]

1 голос
/ 16 мая 2019
  • Каждый раз, когда вы проверяете условие цикла while, вы читаете один символ, а если это не @, вы отбрасываете его. Эти отброшенные символы являются частью данных, которые необходимо проанализировать.

  • Каждый раз, когда вы звоните scanf() и следующий символ является десятичной цифрой или + или -, вы анализируете эту и все последующие цифры как десятичное число, а затем читаете и в конечном итоге отбрасывает следующий символ.

  • Вы не пытаетесь ни прочитать после символов вообще, ни проверить, являются ли они буквами, а не цифрами.

  • Если на входе нет символа @, а в некоторых случаях, даже если он есть, программа никогда не завершится. Чтобы решить эту проблему, он должен проверить на EOF в дополнение к тестированию на '@'. Более того, чтобы сделать это правильно, вы должны сохранить возвращаемое значение getchar() как int; char не может представлять EOF.

Практически ничего из представленного кода не работает так, как описано.

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

0 голосов
/ 17 мая 2019

Как отмечено в комментариях, смешивание scanf и getchar полно подводных камней. Возьмите свой код в качестве примера и свою цель разобрать строку "ABC3DEF@". Ваши звонки на номера getchar и scanf иллюстрируют проблему.

while((cha = getchar()) != '@') {
    scanf("%d %c", &x, &cha);
    ...
}

Когда вы читаете cha, тест (cha = getchar()) != '@' будет ИСТИННЫМ для каждого символа, кроме '@'. Так что для вашей последовательности сначала прочитайте cha = 'A', а "BC3DEF@" останется в stdin. Затем вы пытаетесь прочитать с scanf("%d %c", &x, &cha);, и сбой сопоставления происходит потому, что 'B' не является допустимым началом целочисленного значения, а извлечение символов из stdin перестает оставлять 'B' непрочитанным в stdin. x или cha не являются назначенными значениями.

Затем вы пытаетесь:

    cha = c;
    if(x >= 1 && x <= 9) {

Который устанавливает cha = 0; и вызывает Неопределенное поведение путем доступа к значению x (переменная с автоматической продолжительностью хранения), пока значение не определено. См. C11 Standard - 6.3.2.1 L-значения, массивы и функциональные обозначения (p2) .

Так что все ставки в этот момент сняты. Вы повторяете цикл снова, на этот раз читая 'B' с getchar и ошибка соответствия , и все последующие ошибки повторяются.

Кроме того, тип для cha должен быть int, поскольку это правильный тип возвращаемого значения для getchar и необходим для оценки того, был ли достигнут EOF (который вы не проверяете). См .: человек 3 getchar

Вместо этого полностью исключите scanf из своего кода и просто выполните цикл:

    while ((c = getchar()) != EOF && c != '\n') {
       ...
    }

( Примечание: , я использовал int c;, где вы использовали char cha;)

Затем вы можете обрабатывать все остальное, что необходимо сделать, с тремя основными условиями, например,

    while ((c = getchar()) != EOF && c != '\n') {
        if (isdigit (c)) {              /* if c is digit */
            ...
        }
        else if (isalpha(c)) {          /* if c is [A-Za-z] */
            ...
        }
        else if (c == '@') {            /* if c is end character */
            ...
        }
    }

Оттуда вы просто определяете несколько переменных, которые помогут вам отследить, являетесь ли вы in допустимой последовательностью, является ли последовательность допустимой (lgl), количеством символов, считанных как часть последовательности (nchr) ) и целое число (num), преобразованное в начале последовательности, в дополнение к отслеживанию предыдущего (prev) символа, например,

    char buf[MAXC];
    int c, in = 0, lgl = 1, nchr = 0, num = 0, prev = 0;

При этом вы можете просто читать символьно за разом отслеживая текущее «состояние» операций в вашем цикле,

    while ((c = getchar()) != EOF && c != '\n') {
        if (isdigit (c)) {              /* if c is digit */
            if (!in)                    /* if not in seq. set in = 1 */
                in = 1;
            num *= 10;                  /* build number from digits */
            num += c - '0';
        }
        else if (isalpha(c)) {          /* if c is [A-Za-z] */
            if (in) {
                if (prev >= c)          /* previous char greater or equal? */
                    lgl = 0;            /* not a legal sequence */
                prev = c;               /* hold previous char in order */
                if (nchr < MAXC - 1)    /* make sure there is room in buf */
                    buf[nchr++] = c;    /* add char to buf */
            }
        }
        else if (c == '@') {            /* if c is end character */
            /* if in and legal and at least 1 char and no. char == num */
            if (in && lgl && nchr && nchr == num && num < MAXC) {
                buf[num] = 0;           /* nul-terminate buf */
                printf ("legal: %2d - %s\n", num, buf); /* print result */
            }
            lgl = 1;    /* reset all values */
            in = nchr = num = prev = 0;
        }
    }

В целом, в коротком примере, который сохранит символы для каждой допустимой последовательности в buf, чтобы разрешить вывод последовательности при достижении '@', вы можете сделать что-то похожее на следующее (которое будет обрабатывать последовательности вверх до 8191 символов):

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

#define MAXC 8192   /* if you need a constant, #define one (or more) */
                    /*       (don't skimp on buffer size!)           */
int main (void) {

    char buf[MAXC];
    int c, in = 0, lgl = 1, nchr = 0, num = 0, prev = 0;

    while ((c = getchar()) != EOF && c != '\n') {
        if (isdigit (c)) {              /* if c is digit */
            if (!in)                    /* if not in seq. set in = 1 */
                in = 1;
            num *= 10;                  /* build number from digits */
            num += c - '0';
        }
        else if (isalpha(c)) {          /* if c is [A-Za-z] */
            if (in) {
                if (prev >= c)          /* previous char greater or equal? */
                    lgl = 0;            /* not a legal sequence */
                prev = c;               /* hold previous char in order */
                if (nchr < MAXC - 1)    /* make sure there is room in buf */
                    buf[nchr++] = c;    /* add char to buf */
            }
        }
        else if (c == '@') {            /* if c is end character */
            /* if in and legal and at least 1 char and no. char == num */
            if (in && lgl && nchr && nchr == num && num < MAXC) {
                buf[num] = 0;           /* nul-terminate buf */
                printf ("legal: %2d - %s\n", num, buf); /* print result */
            }
            lgl = 1;    /* reset all values */
            in = nchr = num = prev = 0;
        }
    }
}

( примечание: вы можете настроить MAXC для изменения количества символов по мере необходимости)

Теперь вы можете приступить к проверке кода и убедиться, что он будет соответствовать вашим потребностям в преобразовании (вы можете настроить вывод в соответствии с вашими требованиями). Несмотря на то, что при составлении логики были приняты меры предосторожности, любые угловые случаи, которые, возможно, потребуется обработать, оставлены на ваше усмотрение.

Пример использования / Вывод

$ echo "ABC3DEF@11abcdefghijk@4AZaz@3AbC@" | ./bin/sequences
legal:  3 - DEF
legal: 11 - abcdefghijk
legal:  4 - AZaz

или

$ echo "ABC3DEF@11abcdefghijk@3AbC@4AZaz@" | ./bin/sequences
legal:  3 - DEF
legal: 11 - abcdefghijk
legal:  4 - AZaz

или без окончательного '@' даже иная легальная последовательность будет отброшена

$ echo "ABC3DEF@11abcdefghijk@3AbC@4AZaz" | ./bin/sequences
legal:  3 - DEF
legal: 11 - abcdefghijk

Примечание: если вы действительно хотите принять окончательную допустимую последовательность, даже если до EOF нет закрывающего '@', вы можете просто добавить дополнительное условие после завершения цикла while, например,

    /* handle final sequence before EOF */
    if (in && lgl && nchr && nchr == num && num < MAXC) {
        buf[num] = 0;   /* nul-terminate */
        printf ("legal: %2d - %s\n", num, buf); /* print result */
    }

С этим изменением последний пример, приведенный выше, будет соответствовать выходным данным других.

...