C语言K&R编程:练习3.3

4

例3.3:编写一个函数expand(s1,s2),将字符串s1中的类似于a-z的简写符号扩展成等效的完整列表abc…xyz在s2中。允许大小写字母和数字,并准备处理像a-b-ca-z0-9-a-z这样的情况。确保前导或尾随的-被视为文本。

我正在尝试解决K&R的练习3.3,以下是我的代码:

void expand(char s1[], char s2[]){
    int i; // index for first string
    int j; // index for 2nd string

    for(i = 0, j = 0; s1[i] != '\0'; ++i, ++j){
        if(isalnum(s1[i]) && s1[i+1] == '-'){
            char c = s1[i];
            for(char c = s1[i]; c <= s1[i+2]; ++c, ++j){
                s2[j] = c;
            }
            ++i;
        } else{
            s2[j] = s1[i];
        }
    }
    s2[j] = '\0';
}

它可以成功扩展任何范围,只要它不在任何其他范围之后,即在第一个范围完成后不会向s2添加任何内容。如果我放置这个语句:

printf("%c\n", c);

在第二个循环中,它打印出了正确的字符,但没有将其添加到s2中。
样本输入和输出:
In: akls aldio a-h 19 aodk                                                 
Out: akls aldio abcdefgh

In: 0-6 a-c lol                                                              
Out: 0123456

In: a-c-g 1okd 2-4                                                           
Out: abc

有没有人可以指引我正确的方向来纠正我的错误?
谢谢。


1
欢迎来到Stack Overflow!看起来你可能需要学习如何使用调试器逐行执行代码。有了一个好的调试器,你可以逐行执行程序并查看它与你期望的结果偏离的地方。如果你要进行任何编程工作,这是一个必不可少的工具。更多阅读:如何调试小程序 - Paul R
1
我不确定这是否解决了问题,但我会从两个for循环中删除++j,并将它们放在您写入s2[j]的位置之后,或者更好地使用s2[j++] - Weather Vane
@WeatherVane 那个管用了!有什么想法为什么? - Fazer
1
@PaulR 谢谢。我会研究一下 C 语言的调试器。我以前用过 NetBeans 自带的调试器,但没有用过 C 语言的。 - Fazer
正如答案所说,我的建议是在使用的时候增加索引,而不是在其他地方,以使您更好地理解逻辑。@dbush 也提到了在我之前就已经有了 i+=2。你必须跳过两个字符,而不是一个(第三个字符在 for 循环中处理)。 - Weather Vane
i+=2 对于 a-h-k 这样的情况不起作用。我意识到需要进行另一种修改,但由于这个问题更紧迫,所以我把它放在了后面。 - Fazer
3个回答

3

在内部for循环后,j的位置超过了它应该在的位置,因此跳过了一个位置。如果那个位置恰好包含值0,则会终止字符串并且你看不到它后面的任何内容。

另外,i的位置也是偏前的。

请将以下内容替换为:

        for(char c = s1[i]; c <= s1[i+2]; ++c, ++j){
            s2[j] = c;
        }
        ++i;

使用这个:

        for(char c = s1[i]; c <= s1[i+2]; ++c, ++j){
            s2[j] = c;
        }
        i+=2;
        j--;

1
i 只需要增加一,这样像 a-b-c 这样的情况就可以工作了。其余部分是正确的。它能够工作,并且我明白为什么它不能工作。如果您删除那部分,我将把这个答案标记为正确的。 - Fazer

1

正如dbush所建议的那样,你需要执行j--,但是对于类似a-c-g的情况,为了使其起作用,你还需要更改内部for条件。不再检查c <= s1 [i + 2],而是只需检查到c < s1 [i + 2]

void expand(char s1[], char s2[]){
    int i; // index for first string
    int j; // index for 2nd string

    for(i = 0, j = 0; s1[i] != '\0'; ++i, ++j){
            if(isalnum(s1[i]) && s1[i+1] == '-'){
                    char c = s1[i];
                    /* Do it c < instead of c<= */
                    for(char c = s1[i]; c < s1[i+2]; ++c, ++j){
                            s2[j] = c;
                    }
                    --j;  /* Decrement j once */
                    ++i; 
            } else {
                    s2[j] = s1[i];
            }
    }
    s2[j] = '\0';
}

附言:此代码解决了原帖中显示的输入问题。但是,对于像a-b-c-这样的输入,此代码会失败。还需要额外的代码来处理像abc-DEFabc--defabc-456等输入。然而,正如在本答案的评论中所看到的那样,OP想要自己解决这些问题。


谢谢。在@dbush的答案对这些情况无效后,我意识到并进行了更改。 - Fazer
还有一个问题,就是这段代码有点问题,试着输入 a-g-,它不会起作用... 我正在努力修复它,在我发布答案之前,我只尝试了你的测试输入。 - sps
不要担心。这是我的问题。我还没有完全完成。当我意识到我陷入困境时,我来到了这里。 另外,在你的回答中,你说 c < s1[i+1] 但我认为你的意思是 c < s1[1+2] - Fazer
是的,我指的是 c < s1[i+2],正如代码中所示。 - sps
这个问题很好。我可以在这里为您提供一些好的输入建议。其中一些是“abc-Def”,“abc-456”,“abc--def”,除了通常的输入之外...上面的代码失败了..希望您能修复它们,如果您需要任何指针,可以在此处发送消息回来.... - sps

0

因为 sps 和 dbush 已经让它工作了,所以我也来贡献一下我的思路。

如果可能的话,你应该使你的代码简单易读。例如:你在循环中加载了过多的内容,这不是 IOCCC。只增加/减少你在 for 循环第一部分中定义的索引。 在你的情况下,那就是变量 ic。 对向量 s1s2 的迭代应尽可能接近向量本身。

于是你的代码就变成了:

void expand(char s1[], char s2[]){
    int i;
    int j;
    char c;
    for(i = 0, j = 0; s1[i] != '\0'; i++){
        if(isalnum(s1[i]) && s1[i+1] == '-'){
            for(c = s1[i]; c < s1[i+2]; c++){
                s2[j++] = c;
            }
            i++;
        } else{
            s2[j++] = s1[i];
        }
    }
    s2[j] = '\0';
}

这样做可以避免本来必须进行的修正j--

测试一下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char **argv)
{
  char s[200];
  char s2[200];
  int i;
  memset(s,0,200);
  memset(s2,0,200);
  // put some spaces in between the arguments
  for(i=1;i<argc;i++){
    // counting ommitted!
    strcat(s2,argv[i]);
    s2[strlen(s2)] = ' ';
  }
  printf("In:  %s\n",s2);
  expand(s2,s);
  printf("Out: %s\n",s);
  exit(EXIT_SUCCESS);
}

$ gcc -W -Wall -std=c11  expand.c -o expand
./expand 0-9 ASD a-z QWE a-ch-r
In:  0-9 ASD a-z QWE a-c-r 
Out: 0123456789 ASD abcdefghijklmnopqrstuvwxyz QWE abchijklmnopqr

还有,在 for 循环的第三部分中的 ++i 等等:我们现在是 2016 年。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接