编写仅使用循环和条件语句检查升序的代码

writing a code that checks an ascending sequence only using loops and condition statments only

我被要求用 c 语言编写一个代码来检查输入中的特定字母顺序并确定有多少 "legal" 个订单。顺序是这样的:我收到数字(从 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--;
        }
    }
}
  • 每次测试while循环条件,读取一个字符,如果不是@,则丢弃 它。那些被丢弃的字符是您需要解析的数据的一部分。

  • 每次调用 scanf() 并且下一个字符是十进制数字或 +- 时,您都会解析它和所有后续数字作为十进制数,然后读取 并最终丢弃 下一个字符。

  • 您完全不要尝试读取任何 以下 个字符,也不要测试它们是字母还是数字。

  • 如果输入中没有@字符,在某些情况下即使有一个,程序也永远不会终止。为了解决这个问题,除了测试 '@' 之外,它还应该测试 EOF 。此外,要正确执行此操作,您必须将 getchar() 的 return 值存储为 int 的值; char 不能代表 EOF.

基本上,所提供的代码几乎没有像描述的那样工作。

您需要编写的解析器往往被显式或隐式地编写为状态机。你从一些初始状态开始。您读取输入的一些字符,并根据当前状态和读取的输入执行适当的操作,通常包括将当前状态更改为不同的状态。起泡、冲洗、重复,直到达到最终状态(至少应在输入结束时达到)。

正如评论中指出的那样,混合使用 scanfgetchar 充满陷阱。以你的代码为例,你的目标是解析字符串"ABC3DEF@"。您对 getcharscanf 的调用说明了这个问题。

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

当您阅读 cha 时,测试 (cha = getchar()) != '@' 对除 '@' 之外的每个字符都为真。因此,对于您第一次阅读的序列,cha = 'A'"BC3DEF@" 保留在 stdin 中。然后,您尝试使用 scanf("%d %c", &x, &cha); 进行读取,并且出现 匹配失败 ,因为 'B' 不是整数值的有效开始,并且字符从 stdin 停止在 stdin 中留下 'B' 未读。 xcha 均未分配值。

然后您尝试:

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

通过访问 x(具有自动存储持续时间的变量)的值来设置 cha = 0; 并调用 未定义行为 ,而该值是不确定的。参见 C11 Standard - 6.3.2.1 Lvalues, arrays, and function designators(p2)

所以所有的赌注都在那个时候结束了。你再次循环,这次用 getchar 读取 'B' 匹配失败 并且所有后续错误重复。

此外,cha 的类型必须是 int,因为这是 getchar 的正确 return 类型,并且需要评估 EOF 是否具有已达到(您不检查)。参见:man 3 getchar

相反,从您的代码中完全删除 scanf 并简单地循环使用:

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

(注意:,你用char cha;的地方我用了int c;)

然后您可以处理所有其他需要满足三个主要条件的事情,例如

    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;

有了它,您可以简单地阅读 character-at-a-time 跟踪当前循环中的 "state" 操作,

    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来改变字数)

现在您可以练习代码并确保它能满足您的转换需求(您可以调整输出以满足您的要求)。虽然在将逻辑放在一起时已经很小心,但任何可能需要处理的 corner-cases 都留给你了。

示例Use/Output

$ 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 */
    }

通过该更改,上面的最后一个示例将与其他示例的输出相匹配。