K&R练习1-9:输出输入,用一个空格替换多个空格

sla*_*axx 34 c kernighan-and-ritchie

我一直在研究一些关于C的书,试图得到我的C腿(海腿!得到它?!).我刚从K&R书中完成了练习1-9,其参考是"编写一个程序将其输入复制到其输出中,用一个空格替换一个或多个空格的每个字符串." 我对我的代码发生了什么问题,但是 -

#include <stdio.h>

//Copy input to output. Replace each string of multiple spaces with one single space

int main(int argc, char *argv[]){

    int ch, lch;      // Variables to hold the current and last characters, respectively


    /* This loop should 'put' the current char, then store the current char in lc,
     * loop back, 'get' a new char and check if current and previous chars are both spaces.
     * If both are spaces, do nothing. Otherwise, 'put' the current char
     */

    for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){
            if(ch == ' ' && lch == ' ')
                    ;
            else putchar(ch);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这主要是有效的,除了第一个字符输入.例如,如果第一行输入是

"This        is   a test"
Run Code Online (Sandbox Code Playgroud)

我的代码输出

"his is a test". 
Run Code Online (Sandbox Code Playgroud)

在删除第一个字符输入后,程序始终如一地工作以满足练习的要求.

有人能让我知道我在循环中犯的错误导致了这个问题吗?欢迎任何其他建议.

Am_*_*ful 37

在for循环语句中,您遇到了错误.

for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){...}
Run Code Online (Sandbox Code Playgroud)

在这里,你将第一个字符存储在ch中,然后再次通过再次读取字符输入来测试if(ch!= EOF).

ch=getchar()从初始化声明中删除; 让它在第二部分.

for(;(ch = getchar()) != EOF; lch = ch){...}
Run Code Online (Sandbox Code Playgroud)

此外,您必须在运行之前初始化您的lch,因为在循环的第一次迭代中进行比较之前,lch将不会存储任何值.所以,我们lch=0先做好初始化.

for(lch = 0; (ch = getchar()) != EOF; lch = ch){...}
Run Code Online (Sandbox Code Playgroud)

考虑在编译器中启用警告,它可能会检测并警告此问题,因此您可以修复它.

以上将解决您的问题.

(感谢Blue Moon和hyde帮助我修改答案.)

  • 另一个问题是在第一次迭代期间没有初始化`lch`.导致UB. (4认同)

ale*_*o_o 16

在循环初始化中调用getchar两次:

 for(ch = getchar(); (ch = getchar()) != EOF; lch = ch)
Run Code Online (Sandbox Code Playgroud)

相反,你应该在初始化中调用它一次(获取第一个char),然后在迭代结束时调用它(以获取下一个字符):

int ch, lch = 0; // avoid using uninitialized variable

for(ch = getchar(); ch != EOF; lch = ch)
{
        if(ch == ' ' && lch == ' ')
                ;
        else putchar(ch);

        ch = getchar();
} 
Run Code Online (Sandbox Code Playgroud)

UPD:感谢Blue Moon和shekhar suman用lch指出问题


das*_*ght 13

问题是你的循环的第一次迭代调用了getchar两次 - 一次是在初始化ch变量时,另一次是在检查chEOF.

删除ch = getchar()将解决此问题:

for( lch = '?' ; (ch = getchar()) != EOF; lch = ch) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,您需要lch使用除space之外的任何值进行初始化.


Que*_*tin 9

getchar()在循环开始之前调用一次,然后在for条件中每次迭代调用一次.您检索的第一个字符因此被丢弃.

lch在比较之前,您还需要在循环之前进行初始化.根据您在字符串的第一个字符是空格时要执行的操作:

  • 将其设置为' '将通过"预匹配" 来修剪前导空间.
  • 将其设置为其他任何内容都会正常处理前导空间.

您的循环标题变为(在第二种情况下):

 for(lch = 'a' /*arbitrary*/; (ch = getchar()) != EOF; lch = ch)
Run Code Online (Sandbox Code Playgroud)

感谢shekar suman关于未初始化的单挑lch.


Vla*_*cow 6

改变这个循环

for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){
        if(ch == ' ' && lch == ' ')
                ;
        else putchar(ch);
}
Run Code Online (Sandbox Code Playgroud)

以下方式

for( lch = EOF; ( ch = getchar() ) != EOF; lch = ch )
{
        if ( ch != ' ' || lch != ' ' ) putchar( ch );
}
Run Code Online (Sandbox Code Playgroud)

否则在循环开始时你会读两次字符.

在我看来,作业还描述了另一项任务

"编写一个程序将其输入复制到其输出中,用一个空格替换一个或多个空格的每个字符串."

你应该用一个空格替换每一行空白.:)上面显示的循环不执行此任务.


小智 5

除非任务是使用 for 循环来完成,否则如果您尝试获得更简洁的代码,那么学习该语言会更好。只需告诉自己代码的作用,例如将等效的 while 循环与 for 循环进行比较:

//initialize lch to prevent undefined behaviour
//if the first character is a space, it will be printed
lch = 'A';

// as long as you can read characters
while((ch = getchar()) != EOF) {

    // if either the current character or the previous one is not a space
    if(ch!=' ' || lch!=' ') { 

        //print it
        putchar(ch);
    }

    // remember the current for the next round
    lch = ch;
}
Run Code Online (Sandbox Code Playgroud)

一旦理解了 while 结构,您还可以将其转换为 hacky for 循环,但为什么要这样做呢?while 更容易阅读,编译器不会关心,因为它会以相同的方式编译两者。(大概)