【问题标题】:Squeeze blank lines into one blank line in C在C中将空行挤压成一个空行
【发布时间】:2017-06-09 04:04:05
【问题描述】:

您好,指的是同一个问题,但代码不同。

Replacing multiple new lines in a file with just one

int main(void){

    format();
    printf("\n");
    return 0;
}

void format(){
    int c;
    size_t nlines = 1;
    size_t nspace = 0;

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

        /*TABS*/
        if(c == '\t'){
            c = ' ';
        }
        /*SPACES*/
        if (c ==' '){
            if(nspace > 0){
                continue;
            }
            else{
                putchar(c);
                nspace++;
                nlines = 0;
            }
        }

        /*NEW LINE*/
        else if(c == '\n'){
            if(++nlines >2){
                continue;
            }
            else {
                nlines++;
                nspace = 0;
            }
            putchar(c);
        }   
        else{
            putchar(c);
            nspace = 0;
            nlines = 0;
        }       
    }
}

我想将多个空白行压缩到一个空白行中,但它似乎不起作用,并且在标准输出的 Cygwin 终端上,最后一行给了我额外的空白行,尽管输入末尾没有空白行.

例如
输入

Hello   Hi\n
\n
\n
Hey\t\tHola\n

期望的输出

Hello Hi\n
\n
Hey Hola\n

实际输出

Hello Hi\n
Hey Hola\n

请解释一下!

【问题讨论】:

  • 您提供的输出是您想要的输出或您正在获得的输出。
  • isspace() 可以让你的代码更加简洁易读
  • 不要不要在获得答案后修改您的代码,从而使答案无效。这根本不是犹太教。
  • 我的导师不喜欢它。我使用了嵌套的 while 循环,它有效,但我不得不重做,因为他不喜欢它。还是非常感谢! @VolAnd
  • 哦,我很抱歉!我认为这对人们来说会很棒。我很抱歉。 @JonathanLeffler

标签: c


【解决方案1】:

这是您的代码的变体。我删除了 format() 函数(这对我来说很不寻常,因为 SO 上的大多数程序都没有使用足够的函数)将它直接合并到 main() 中。该代码现在更对称地处理空格和换行符,修复了在paddyanswer 中也发现的双增量问题。如果末尾还没有换行符,它也只会在末尾打印出换行符。这会规范不以换行符结尾的文件。 nlines = 1; 的初始化处理文件开头的多个换行符——这已经做得很好了。

#include <stdio.h>

int main(void)
{
    int c;
    size_t nlines = 1;
    size_t nspace = 0;

    while ((c = getchar()) != EOF)
    {
        if (c == '\t')
            c = ' ';
        if (c == ' ')
        {
            if (nspace < 1)
            {
                putchar(c);
                nspace++;
                nlines = 0;
            }
        }
        else if (c == '\n')
        {
            if (nlines < 2)
            {
                putchar(c);
                nlines++;
                nspace = 0;
            }
        }
        else
        {
            putchar(c);
            nspace = 0;
            nlines = 0;
        }
    }
    if (nlines == 0)
        putchar('\n');
    return 0;
}

我的测试使用了一些特定于 Bash 的符号。我的程序是sb73: 最后一个测试输入不包括最后的换行符。输出使用 ⌴ 表示输出中的换行符:

$ echo $'Hello   Hi\n\n\nHey\t\tHola\n' | sb73
Hello Hi⌴
⌴
Hey Hola
⌴
$

和:

$ echo $'\n\nHello   Hi\n\n\n    Hey\t\tHola\n' | sb73
⌴
Hello Hi⌴
⌴
 Hey Hola⌴
⌴
$

和:

$ printf '%s' $'\n\nHello   Hi\n\n\n    Hey\t\tHola' | sb73
⌴
Hello Hi⌴
⌴
 Hey Hola⌴
$

处理 CRLF 行尾

cmets 发现上面的代码在 Cygwin 终端上不起作用,可能的原因是正在修改的数据有 CRLF 行结尾。有多种解决方法。一种是找到一种强制标准输入进入文本模式的方法。在文本模式下,输入时应将 CRLF 行结尾映射到 Unix 样式的 '\n'(仅限 NL 或 LF)结尾,而输出时应将 Unix 样式的行结尾映射到 CRLF 行结尾。

或者,可以简单地忽略 CR 字符:

--- sb73.c  2017-06-08 22:04:28.000000000 -0700
+++ sb47.c  2017-06-08 22:40:24.000000000 -0700
@@ -19,6 +19,8 @@
                 nlines = 0;
             }
         }
+        else if (c == '\r')
+            continue;    // Windows?
         else if (c == '\n')
         {
             if (nlines < 2)

这是一个“统一差异”,在代码中显示了两行额外的行。或者可以将不跟在 LF 后面的 CR 当作常规字符处理,而将 CR 后面跟 LF 当作换行符组合处理:

--- sb73.c  2017-06-08 22:04:28.000000000 -0700
+++ sb59.c  2017-06-08 22:42:43.000000000 -0700
@@ -19,6 +19,17 @@
                 nlines = 0;
             }
         }
+        else if (c == '\r')
+        {
+            if ((c = getchar()) == '\n')
+            {
+               ungetc(c, stdin);
+               continue;
+            }
+            putchar('\r');
+            nspace = 0;
+            nlines = 0;
+        }
         else if (c == '\n')
         {
             if (nlines < 2)

可能有一种方法可以编写处理 CR 的状态机,但这会更复杂。

我有一个utod 程序,可以将 Unix 风格的行尾转换为 Windows 风格;我在管道中使用它来测试代码的新变体。

【讨论】:

  • 非常感谢!现在在我的 Cygwin 终端上可以正常工作了!它没有给我额外的空行
  • 有趣的是,它在我的 Cygwin 终端上不起作用。它不会挤压多个空行。
  • 在 Windows 上,如果标准输入不是“文本”模式,您将获得一个 '\r' 和一个 '\n' 用于每一行结尾,并且显示的代码将 '\r' 视为一个 '其他'字符。可以对其进行修改以处理 CRLF。一种简单的方法是完全忽略'\r'。另一种可能性是有一种特定于 Windows 的方法可以将标准输入置于文本模式。或者可以进行更彻底的修改。
  • @JonathanLeffler 有setmode(),但stdin 默认应该是文本模式...
  • @FelixPalmen:感谢函数名称——我不需要记住它。我有点惊讶标准输入不是文本模式,但可能与 Cygwin 有关。 OTOH,这似乎是对出了什么问题的合理解释,因为如果发生 CRLF 到换行符的映射,显示的代码应该可以正常工作。可能是别的东西,但我会很惊讶。
【解决方案2】:

您将nlines 增加两次:

else if(c == '\n'){
    if(++nlines >2){  /* incremented here */
        continue;
    }
    else {
        nlines++;     /* incremented here */
        nspace = 0;
    }
    putchar(c);
}

你只想做一次。我建议只增加计数器,直到它达到 2,然后不再增加它。这只是一个小小的改变:

    if(nlines >= 2){
        continue;
    }

【讨论】:

  • 你好我修改了我的工作代码,除了 Cygwin 终端,它给了我额外的结束行。
  • @dyingStudent:您的代码无条件地在输出末尾添加换行符——这显然不是您想要的。但是,您的代码也无法识别文件是否在没有换行符的情况下结束。您可以在我的回答中看到我是如何解决的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 2017-11-09
  • 1970-01-01
  • 1970-01-01
  • 2020-05-28
相关资源
最近更新 更多