【问题标题】:Does this small C program satisfy the K&R exercise?这个小 C 程序是否满足 K&R 练习?
【发布时间】:2010-08-12 13:00:12
【问题描述】:

我正在学习 K&R 的练习 1-18

编写一个程序来删除每行输入的尾随空格和制表符,并删除整个空白行。

这是我目前想出的

#include <stdio.h>

#define MAXLINE 1000

int getline(char line[], int maxline);
void copy(char to[], char from[]);

int main () {

    int len;
    char line[MAXLINE];

    while (getline(line, MAXLINE) > 0) {
            printf("%s", line);
    }
    return 0;
}


int getline(char s[], int lim) {
    int c, i, lastNonBlankIndex;
    lastNonBlankIndex = 0;

    for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {

        if (c != ' ' && c != '\t') {
            lastNonBlankIndex = i + 1;

        } 

        s[i] = c;
    }

    if (i != lastNonBlankIndex) {
        i = lastNonBlankIndex;
        c = '\n';
    }

    if (c == '\n') {
        s[i] = c;   
        ++i;
    }
    s[i] = '\0';
    return i;
}

第二部分听起来很难,因为如果该行只有空格或制表符,我不确定应该返回什么。毕竟,如果我返回 0,它将停止 getline() 调用。这是我应该设置#define的地方吗,比如ALL_BLANKS。

无论如何,对于实际的主要问题,这是从行中删除尾随空格和制表符的正确方法吗?我运行了一些输入,它似乎起作用了。但是,如果我将带有换行符的文本复制并粘贴到 CL 中,它会看起来全部串在一起。当我在 CL 中输入一行并按下回车时,它会自动打印出来。我应该构建一个行数组,然后在完成后循环并打印它们吗?

【问题讨论】:

  • 迂腐点:使用i + 1&lt; lim。这不会导致整数溢出显示代码愚蠢地调用getline(s, INT_MIN),并且它不会比原始代码更多。

标签: c kernighan-and-ritchie


【解决方案1】:

您的代码看起来是正确的,但我认为如果您将读取一行的操作从stdin 中分离出来并去除尾随空格的行(解耦)会更好。然后你可以使用书中未修改的getline(代码重用),并且不会有返回0时停止的问题。

如果您对其他解决方案感兴趣,CLC-wiki 有一个几乎完整的 K&R2 solutions 列表。

#include <stdio.h>
#define MAXLINE 1024

int getline(char s[], int lim);

main()
{
    int i, len;
    char line[MAXLINE];

    while ((len = getline(line, MAXLINE)) > 0) {
        i = len - 2;
        while (i >= 0 && (line[i] == ' ' || line[i] == '\t'))
            --i;
        if (i >= 0) {
            line[i+1] = '\n';
            line[i+2] = '\0';
            printf("%s", line);
        }
    }
    return 0;
}

这是我前段时间写的第 1 类解决方案。 getline 与本书第 28 页相同。将删除空格放在单独的函数rstrip 中可能会更好,但我将其留给读者作为练习。

【讨论】:

  • @alex 不要作弊 ;) 顺便说一句,我有一个自己的旧解决方案,我可以添加到我的答案中,但前提是您认为它有用。
  • 别担心,我正在尝试为自己学习,所以查看答案并复制它对我没有帮助!随时发布您的解决方案,谢谢。如果你想看看,我也在尝试next exercise!
【解决方案2】:

您的基本设计是合理的。正如您所做的那样,最好在构建后立即打印剥离的行,这样您的程序只需在内存中一次保留一行,而不是整个文件。

您的代码有一个小问题:它没有实现问题的第二部分(“删除完全空白行”)。那是因为你总是在字符串的末尾添加一个'\n'。这很容易解决,但请记住,您必须向调用者返回一个非零值,因为空行并不表示文件结束。

【讨论】:

    【解决方案3】:

    getline 如果出现错误或达到 EOF,则应返回 -1(通常为负值)。然后你的循环条件可以检查它是否返回 &gt;= 0 并且仍然允许 0 长度的行。

    for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
    

    我几乎从不在循环条件中包含赋值。我宁愿添加 10 行代码来解决这个问题,因为它很难阅读。我会特别避免将它们与复杂的条件一起使用。

    int i = 0;
    
    while (i < lim) {
       c = getchar();
       if (c == EOF || c == '\n') {
           break;
       }
       line[i] = (char)c;
       i++;
    }
    line[i] = '\0'; // Null terminate the string
    

    这段代码应该读成一行。我会将行的读取与尾随空格的删除分开。您可以很容易地从字符串的末尾向后工作,以删除我空终止该行的位置的空格,因为在读入该行之后,您现在知道它的长度。从本质上讲,您会长出绳子,然后在完成长出后将其修剪掉。

    【讨论】:

    • 我在那里使用 assignment 的唯一原因是因为 K&R 书确实......但我确实明白你的意思,它很难阅读!
    【解决方案4】:

    我就是这样做的。

    #include <stdio.h>
    #define MAXLINE 1000
    
    #define IN 1
    #define OUT 0
    
    int state = OUT;
    
    int getline(char s[], int lim);
    void copy(char to[], char from[]);
    
    int main(void)
    {
    int lenght;
    int max = 0;
    char line[MAXLINE];
    char longest[MAXLINE];
    
    while ((lenght = getline(line, MAXLINE)) > 0)
        if (lenght > max)
        {
            max = lenght;
            copy(longest, line);
        }
    if (max > 0)
        printf("\n%s\n", longest);
    
        return 0;
    
        }
       int getline(char s[], int lim)
       {
       int i, c;
       for (i = 0; i < lim - 1 && ((c = getchar()) != EOF) && (c != '\n'); i++)
       {
    
    
        if (state == IN && c != ' ' && c != '\t')
        {
            s[i] = ' ';
            i++;
            state = OUT;
        }
        if (s[0] == ' ')
        {
            s[0] = '\b';
        }
    
    
        s[i] = c;
    
    
        if (c == ' ' || c == '\t')
        {
            i--;
            state = IN;
        }
    }
    if (c == '\n')
    {
        s[i] = c;
        i++;
    }
    s[i] = '\0';
    
    return i;
     }
    
    void copy(char to[], char from[])
    {
    int i = 0;
    while ((to[i] = from[i]) != '\0')
        i++;
    }
    

    【讨论】:

      【解决方案5】:
      #include <stdio.h>
      
      #define MAXLINE 1000
      
      size_t getline(char *s,size_t lim)
      {
        if( fgets(s,lim,stdin) )
        {
          while( *s && strchr(" \t\n",s[strlen(s)-1]) )
            s[strlen(s)-1]=0;
          return strlen(s);
        }
        return 0;
      }
      
      main()
      {
          int len;
          char line[MAXLINE];
      
          while (getline(line,sizeof line)) {
                  printf("%s", line);
          }
          return 0;
      }
      

      【讨论】:

      • -1:这根本不打印换行符,遇到空行后停止,错过#include &lt;string.h&gt;,并且使用了很多K&R2第1章中尚未解释的概念(指针, sizeof, str*).
      猜你喜欢
      • 1970-01-01
      • 2020-06-25
      • 1970-01-01
      • 2015-01-14
      • 1970-01-01
      • 1970-01-01
      • 2013-03-25
      • 1970-01-01
      • 2016-04-12
      相关资源
      最近更新 更多