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

时间:2015-07-23 21:00:47

标签: 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;
}

除了第一个字符输入外,这大部分都有效。例如,如果第一行输入是

"This        is   a test"

我的代码输出

"his is a test". 

在删除第一个字符输入后,程序会一直工作以满足练习的要求。

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

14 个答案:

答案 0 :(得分:36)

在for-loop语句中,您有错误。

for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){...}

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

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

for(;(ch = getchar()) != EOF; lch = ch){...}

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

for(lch = 0; (ch = getchar()) != EOF; lch = ch){...}

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

以上可以解决您的问题。

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

答案 1 :(得分:16)

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

 for(ch = getchar(); (ch = getchar()) != EOF; lch = ch)

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

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

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

        ch = getchar();
} 

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

答案 2 :(得分:13)

问题在于,循环的第一次迭代会调用getchar两次 - 一次初始化ch变量时,再一次检查chEOF

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

for( lch = '?' ; (ch = getchar()) != EOF; lch = ch) {
    ...
}

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

答案 3 :(得分:9)

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

在比较之前,您还需要在循环之前初始化lch。当字符串的第一个字符是空格时,取决于您想要做什么:

  • 将其设置为' '将修改前导空格&#34;预匹配&#34;它。
  • 将其设置为其他任何内容都会正常处理领先空间。

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

 for(lch = 'a' /*arbitrary*/; (ch = getchar()) != EOF; lch = ch)

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

答案 4 :(得分:6)

更改此循环

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

以下方式

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

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

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

  

&#34;编写程序将其输入复制到其输出,替换每个   一个空格的一个或多个空格的字符串。&#34;

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

答案 5 :(得分:4)

除非任务是使用for循环执行,否则如果您尝试获得更清晰的代码,那么学习该语言会更好。只需告诉自己代码的作用,比较一下与for循环等效的while循环:

//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;
}

一旦你理解了while-construct,你也可以将它转换为hacky for-loop,但为什么会这样? while更容易阅读,编译器不关心,因为它将以相同的方式编译。 (可能)

答案 6 :(得分:4)

虽然有很多正确的答案,但是让我给你一个提示,告诉你如何使用调试器(gdb here)自行跟踪这个问题:

首先将代码更改为这样(每行一个语句!):

...

for(ch = getchar(); 
   (ch = getchar()) != EOF; 
   lch = ch){

...

现在使用符号(g {的-g)编译它,然后使用调试器运行代码:

 gdb ./a.out

main()

处设一个断点
(gdb) break main

启动程序:

(gdb) run

看到它停在main()

Breakpoint 1, main (argc=1, argv=0x7fffffffe448) at main.c:15
15      for(ch = getchar(); 
(gdb) 

逐步完成代码:

(gdb) step

使用gbd命令行中的print ch在“运行”代码的各个阶段检查有趣的变量(ch),同时逐步执行。

有关如何操纵gbd的更多详细信息:http://beej.us/guide/bggdb/

答案 7 :(得分:3)

是的,当你宣布你的声明时,首先你要用

初始化ch。
for( ch= getchar();

所以此时你得到你的第一个字符(T)并且指针将一个位置前进到下一个字符(h)

然后你再次使用(ch = getchar()) !=EOF;

获取字符

尝试更改for (ch= getchar();并改为使用for (ch= '' ;

希望能解决它。

答案 8 :(得分:1)

for语句有三个部分:初始化,条件和增量。这些部分用两个分号分隔。

for语句的条件部分有副作用时,这非常令人困惑。副作用属于增量部分:

for (ch = getchar(); ch != EOF; lch = ch, ch = getchar())

并且,正如其他人所指出的那样,lch必须被初始化,所以:

int lch ='a';

最后,虽然这不会影响程序的正确性,但我会反转if测试:

if (ch != ' ' || lch != ' ')
    putchar(ch);

答案 9 :(得分:0)

这对我有用

      <?php
 class Rsl_Icon_For_Title {

    public $myposts;

    public function rsl_settings_page_get_data($myposts) {
        $my_posts = new WP_Query;
        $this->myposts = $my_posts->query( array('post_type' => 'post'));
        return $this->myposts;

    }
    public function rsl_settings_page_render () {

      $this->rsl_settings_page_get_data();
      var_dump($this->my_posts);


    }
 }

答案 10 :(得分:0)

@elessar的更改不大。第12行必须从(blank> 1)更改为(blank> = 1),因为前一行不会打印单个空格。

#include <stdio.h>
int main(int arg, char *argv[]){
char c = 0;
long blank = 0;
long tab   = 0;
while((c=getchar())!= EOF){
 if(c == ' '){
    ++blank;  
 }

 if(c != ' '){
     if(blank>=1){
       printf("%c", ' ');
       blank = 0;
       printf("%c", c);            
        }
 else{
        printf("%c", c);                            
     }
  }    

 } //end of while
return 0;
}

答案 11 :(得分:0)

另一种嘲讽:

#include <stdio.h>

int main()
{
    int charac;

    // Variable declared for verifying consecutive whitespaces 
    bool blank = false;

    // As long as you did not input EOF (Ctrl + Z on Windows, Ctrl + D on linux, macOS)
    while ((charac = getchar()) != EOF){

        // Current char is whitespace, the one before was also whitespace => go to next iteration    
        if((charac == ' ') && (blank == true)){
            continue;
        }
        // If current char is whitespace, keep this in mind(blank = true) and output the whitespace
        else if(charac == ' ')
        {
            blank = true;
            putchar(charac);
            continue;
        }

        // If current character is not whitespace, output it and reset the blank boolean
        putchar(charac);
        blank = false;
    }

    return 0;
}

答案 12 :(得分:0)

#include <stdio.h>
#include <ctype.h>

/* replace each string of one or more blanks by a single blank */
int main() {
    int c, s1;

    s1 = 0;
    while ((c = getchar()) != EOF) {
        if (isspace(c)) {
            ++s1;
        } else {
            s1 = 0;
        }
        if (s1 > 1) {
            continue;
        }
        putchar(c);
    }

    return 0;
}

答案 13 :(得分:0)

我也在阅读这本书学习 C 并且我设法想出了这种方法,我希望得到一些反馈以改进。 我尽量不声明太多变量,以免浪费内存空间。 我最终定义了毯子空间以便稍后打印,因为我想将多个制表符和空格视为一种情况。

#include <stdio.h>

/* space char was defined so I can treat ' ' and '\t' on the same case */
#define BLANK ' '


int main(){

    int c;

    while((c = getchar()) != EOF){

        /* if char is either ' ' or '\t' */
        if((c == ' ') || (c == '\t')){

            /* print a blank */
            putchar(BLANK);
            /* read next char */
            c = getchar();

            /* while after the ' ' or '\t' the char is again ' ' or '\t' ... */
            /* I'm not going to bother with it and I'm going to read the next char */
            while((c == ' ') || (c == '\t')){
                c=getchar();
            }

            /* print the char */
            putchar(c);
        }

        /* another char */
        else {
            putchar(c);
        }
    }

    
}