编写仅使用循环和条件语句检查升序的代码
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
.
基本上,所提供的代码几乎没有像描述的那样工作。
您需要编写的解析器往往被显式或隐式地编写为状态机。你从一些初始状态开始。您读取输入的一些字符,并根据当前状态和读取的输入执行适当的操作,通常包括将当前状态更改为不同的状态。起泡、冲洗、重复,直到达到最终状态(至少应在输入结束时达到)。
正如评论中指出的那样,混合使用 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
停止在 stdin
中留下 'B'
未读。 x
或 cha
均未分配值。
然后您尝试:
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 */
}
通过该更改,上面的最后一个示例将与其他示例的输出相匹配。
我被要求用 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
.
基本上,所提供的代码几乎没有像描述的那样工作。
您需要编写的解析器往往被显式或隐式地编写为状态机。你从一些初始状态开始。您读取输入的一些字符,并根据当前状态和读取的输入执行适当的操作,通常包括将当前状态更改为不同的状态。起泡、冲洗、重复,直到达到最终状态(至少应在输入结束时达到)。
正如评论中指出的那样,混合使用 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
停止在 stdin
中留下 'B'
未读。 x
或 cha
均未分配值。
然后您尝试:
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 */
}
通过该更改,上面的最后一个示例将与其他示例的输出相匹配。