5

我开始 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 中输入一行并按 Enter 时,它会自动打印出来。我应该构建一个行数组,然后在完成后循环并打印它们吗?

4

5 回答 5

5

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

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

#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,但我将其作为练习留给读者。

于 2010-08-12T13:05:41.530 回答
0

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

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

于 2010-08-12T13:17:08.697 回答
0

我就是这样做的。

#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++;
}
于 2015-04-22T21:47:10.797 回答
0

getline如果出现错误或达到 EOF,则应返回 -1(通常为负值)。然后你的循环条件可以检查它是否返回一些东西>= 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

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

于 2010-08-12T15:22:34.270 回答
-1
#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;
}
于 2010-08-12T13:41:48.617 回答