Как отмечено в комментариях, смешивание 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 */
}
С этим изменением последний пример, приведенный выше, будет соответствовать выходным данным других.