【问题标题】:Replacing multiple new lines in a file with just one仅用一个替换文件中的多个新行
【发布时间】:2015-06-26 05:38:04
【问题描述】:

这个函数应该在一个文本文件中搜索换行符。当它找到换行符时,它会增加newLine 计数器,当有超过 2 个连续的空白新行时,它假设将所有空白行压缩到一个空白行中。

在我的代码中,如果有 2 条新行,则假设将它们删除并将它们压缩为一条,出于测试目的,我还让它在达到 newLine < 2 条件时打印“新行”。现在它为每个新行打印新行,无论它是否为空白,并且它没有摆脱额外的新行。我究竟做错了什么?

编辑:这是我的完整代码 http://pastebin.com/bsD3b38a

因此,基本上该程序假设将两个文件连接在一起,然后对它们执行各种操作,就像我正在尝试做的那样,摆脱多个连续的空白新行。 所以为了在cygwin中执行它我做 ./a -s 文件1 文件2 它假设将 file1 和 file2 连接到一个名为 contents.txt 的文件中,然后去掉连续的新行并将它们显示在我的 cygwin 终端(stdout)上。 ( -s 调用该函数以摆脱连续的行)。传入的第三个和第四个参数(file1 和 file2)是两个文件,它假设要连接在一起形成一个名为 contents.txt 的文件。您可以在下面查看我放入 file1.txt 的内容示例。 file2.txt 只有一堆单词,后面跟着空的新行。

int newLine = 1;
int c; 

if ((fileContents = fopen("fileContents.txt", "r")) == 0) 
{
    perror("fopen");
    return 1; 
}

while ((c = fgetc(fileContents)) != EOF)
{   
    if (c == '\n')
    {
        newLine++;
        if (newLine < 2) 
        {
            printf("new line");
            putchar(c); 
        }
    }
    else 
    {
        putchar(c); 
        newLine = 0;
    }
}

程序在包含这些内容的 .txt 文件中读取的文件。它假设读取文件,摆脱前导和连续的新行,并将新格式化的内容输出到我的 cywgin 终端上的标准输出。

/* hello world program */


#include <stdio.h>

    tab
            2tabs

【问题讨论】:

  • 您确定c 必须是int
  • 我认为你的代码逻辑是正确的。 1)通过定义newLine = 1,它将去掉输入txt的任何前导'\n'。 2)当有几个连续的新行时,它只会输出一个'\n'。
  • @Sinstein:是的,cint 至关重要,因为 fgetc()getc()getchar() 都返回 int 而不是 char。你可以找到很多涵盖这一点的问题。
  • @Sinstein:int vs char 的一个例子是while ((c = getc(file)) != EOF) loop won't stop executing

标签: c file


【解决方案1】:

诊断

逻辑看起来是正确的如果你有 Unix 行结尾。如果您有 Windows CRLF 行结尾但正在 Unix 上处理文件,则在每个 LF 之前都有一个 CR,并且 CR 将 newLine 重置为零,因此您会收到每个换行符的消息。

这可以解释你所看到的。

这也可以解释为什么其他人都说你的逻辑是正确的(它是正确的——前提是这些行只以 LF 而不是 CRLF 结尾)但是你看到了一个意想不到的结果。

如何解决?

公平的问题。一个主要选项是使用dos2unix 或等效机制将DOS 文件转换为Unix 文件。关于 SO 的主题有很多问题。

如果您根本不需要 CR(C 中的 '\r')字符,您可以简单地删除(不打印,而不是零 newLine)这些字符。

如果您需要保留 CRLF 行尾,则需要更加小心。你必须记录你有一个 CR,然后检查你是否有一个 LF,然后打印这对,然后检查你是否有更多的 CRLF 序列并抑制这些,等等。

工作代码——dupnl.c

这个程序只从标准输入读取;这比更灵活 仅从固定文件名读取。学会避免编写代码 仅适用于一个文件名;它会为你节省大量的重新编译 随着时间的推移。代码仅处理带有换行符 ("\n") 的 Unix 样式文件 在末尾;它还处理带有 CRLF ("\r\n") 结尾的 DOS 文件;和 它还使用 CR 处理(旧式)Mac(Mac OS 9 及更早版本)文件 ("\r") 行尾。事实上,它处理任意交错 不同的行尾样式。如果你想执行一个单一的 模式,你必须做一些工作来决定哪种模式,然后使用 此代码的适当子集。

#include <stdio.h>

int main(void)
{
    FILE *fp = stdin;       // Instead of fopen()
    int newLine = 1;
    int c; 

    while ((c = fgetc(fp)) != EOF)
    {   
        if (c == '\n')
        {
            /* Unix NL line ending */
            if (newLine++ == 0)
                putchar(c); 
        }
        else if (c == '\r')
        {
            int c1 = fgetc(fp);
            if (c1 == '\n')
            {
                /* DOS CRLF line ending */
                if (newLine++ == 0)
                {
                    putchar(c);
                    putchar(c1);
                }
            }
            else
            {
                /* MAC CR line ending */
                if (newLine++ == 0)
                    putchar(c);
                if (c1 != EOF && c1 != '\r')
                    ungetc(c1, stdin);
            }
        }
        else
        {
            putchar(c); 
            newLine = 0;
        }
    }

    return 0;
}

示例运行 - 输入和输出

$ cat test.unx


data long enough to be seen 1 - Unix

data long enough to be seen 2 - Unix
data long enough to be seen 3 - Unix
data long enough to be seen 4 - Unix



data long enough to be seen 5 - Unix


$ sed 's/Unix/DOS/g' test.unx | ule -d > test.dos
$ cat test.dos


data long enough to be seen 1 - DOS

data long enough to be seen 2 - DOS
data long enough to be seen 3 - DOS
data long enough to be seen 4 - DOS



data long enough to be seen 5 - DOS


$ sed 's/Unix/Mac/g' test.unx | ule -m > test.mac
$ cat test.mac
$ ta long enough to be seen 5 - Mac
$ odx test.mac
0x0000: 0D 0D 64 61 74 61 20 6C 6F 6E 67 20 65 6E 6F 75   ..data long enou
0x0010: 67 68 20 74 6F 20 62 65 20 73 65 65 6E 20 31 20   gh to be seen 1 
0x0020: 2D 20 4D 61 63 0D 0D 64 61 74 61 20 6C 6F 6E 67   - Mac..data long
0x0030: 20 65 6E 6F 75 67 68 20 74 6F 20 62 65 20 73 65    enough to be se
0x0040: 65 6E 20 32 20 2D 20 4D 61 63 0D 64 61 74 61 20   en 2 - Mac.data 
0x0050: 6C 6F 6E 67 20 65 6E 6F 75 67 68 20 74 6F 20 62   long enough to b
0x0060: 65 20 73 65 65 6E 20 33 20 2D 20 4D 61 63 0D 64   e seen 3 - Mac.d
0x0070: 61 74 61 20 6C 6F 6E 67 20 65 6E 6F 75 67 68 20   ata long enough 
0x0080: 74 6F 20 62 65 20 73 65 65 6E 20 34 20 2D 20 4D   to be seen 4 - M
0x0090: 61 63 0D 0D 0D 0D 64 61 74 61 20 6C 6F 6E 67 20   ac....data long 
0x00A0: 65 6E 6F 75 67 68 20 74 6F 20 62 65 20 73 65 65   enough to be see
0x00B0: 6E 20 35 20 2D 20 4D 61 63 0D 0D 0D               n 5 - Mac...
0x00BC:
$ dupnl < test.unx
data long enough to be seen 1 - Unix
data long enough to be seen 2 - Unix
data long enough to be seen 3 - Unix
data long enough to be seen 4 - Unix
data long enough to be seen 5 - Unix
$ dupnl < test.dos
data long enough to be seen 1 - DOS
data long enough to be seen 2 - DOS
data long enough to be seen 3 - DOS
data long enough to be seen 4 - DOS
data long enough to be seen 5 - DOS
$ dupnl < test.mac
$ ta long enough to be seen 5 - Mac
$ dupnl < test.mac | odx
0x0000: 64 61 74 61 20 6C 6F 6E 67 20 65 6E 6F 75 67 68   data long enough
0x0010: 20 74 6F 20 62 65 20 73 65 65 6E 20 31 20 2D 20    to be seen 1 - 
0x0020: 4D 61 63 0D 64 61 74 61 20 6C 6F 6E 67 20 65 6E   Mac.data long en
0x0030: 6F 75 67 68 20 74 6F 20 62 65 20 73 65 65 6E 20   ough to be seen 
0x0040: 32 20 2D 20 4D 61 63 0D 64 61 74 61 20 6C 6F 6E   2 - Mac.data lon
0x0050: 67 20 65 6E 6F 75 67 68 20 74 6F 20 62 65 20 73   g enough to be s
0x0060: 65 65 6E 20 33 20 2D 20 4D 61 63 0D 64 61 74 61   een 3 - Mac.data
0x0070: 20 6C 6F 6E 67 20 65 6E 6F 75 67 68 20 74 6F 20    long enough to 
0x0080: 62 65 20 73 65 65 6E 20 34 20 2D 20 4D 61 63 0D   be seen 4 - Mac.
0x0090: 64 61 74 61 20 6C 6F 6E 67 20 65 6E 6F 75 67 68   data long enough
0x00A0: 20 74 6F 20 62 65 20 73 65 65 6E 20 35 20 2D 20    to be seen 5 - 
0x00B0: 4D 61 63 0D                                       Mac.
0x00B4:
$

$ ta 开头的行是提示覆盖先前输出的地方(而“足够长以被看到”部分是因为我的提示通常比$ 更长)。

odx 是一个十六进制转储程序。 ule 用于“统一行尾”并分析或转换数据,使其具有统一的行尾。

Usage: ule [-cdhmnsuzV] [file ...]
  -c  Check line endings (default)
  -d  Convert to DOS (CRLF) line endings
  -h  Print this help and exit
  -m  Convert to MAC (CR) line endings
  -n  Ensure line ending at end of file
  -s  Write output to standard output (default)
  -u  Convert to Unix (LF) line endings
  -z  Check for zero (null) bytes
  -V  Print version information and exit

【讨论】:

  • 由于不同的新行定义如 --DOS & Windows: \r\n 0D0A , Unix & Mac OS X: \ n, 0A,Macintosh (OS 9): \r, 0D.Thanks.
  • 这可能会成为一篇重要的文章,@EricTsui。存在多个问题,从“具有本机行尾的文本模式文件在读取时映射到换行符结尾,而换行符在写入时映射到本机”(这当然也会影响诸如fgets() 之类的功能),到机制从具有不确定结尾的文件中读取行(您不能使用fgets() 甚至POSIX getline(),因为他们只知道本机行结尾)等等。我有一个程序ule(统一行结尾)可以分析行结尾并转换为 DOS、Mac 或 Unix(以及其他一些技巧)。
  • @Jonathan 您好,感谢您的回复。我不确定我是否也要检查 '\r'。该程序正在读取“.txt”文件并在其中搜索连续的新行。我正在使用在 Windows 8.1 机器上运行的 cygwin 终端。虽然我认为这不重要。我已经将我的完整代码添加到原始帖子中,并解释了它是如何工作的,如果你愿意看的话。谢谢。
  • 哦,是的,这很重要!事实上,这很重要。由于您在 Windows 上使用 Cygwin,因此您将主要使用 Windows (DOS) 样式的 CRLF 行尾。 Cygwin 代码在输入期间没有将 CRLF 映射到换行符的可能性很高,因此我的分析可能适用。最简单的检查方法是在if (c == '\n') 测试之前添加if (c == '\r') puts("\nCR");。然后您会看到 CR 出现在“新行”消息之前。我在 CR 之前包含了 \n,在之后包含了一个隐含的,因为 putchar(c)c == '\r' 将覆盖行首的内容。
  • @Jonathan 好吧,据我了解,我的代码应该看起来像这样pastebin.com/FbuMLbXR 所有这一切都是在每 2 行添加 CR,并添加了一堆不在原始行中的新行文件
【解决方案2】:

示例代码解析的是:

1) 将连续的几个 '\n' 压缩成一个 '\n'

2) 如果有的话,去掉开头的'\n'。

  input:   '\n\n\naa\nbb\n\ncc' 
  output:   aa'\n'    
            bb'\n' //notice, there is no blank line here
            cc

如果这是目标,那么你的代码逻辑是正确的。

  • 通过定义 newLine = 1 ,它将去掉任何前导的 '\n' 输入txt。

  • 并且当处理后还有一个'\n'时,会输出一个new line进行提示。

回到问题本身,如果实际目的是将连续的空行压缩到一个空行(需要两个连续的'\n',一个用于终止前一行,一个用于空行)。

1) 我们先确认输入和预期输出,

输入文字:

aaa'\n' //1st line, there is a '\n' append to 'aaa'  
'\n'    //2nd line, blank line
bbb'\n' //3rd line, there is a '\n' append to 'bbb'
'\n'    //4th line, blank line
'\n'    //5th line, blank line
'\n'    //6th line, blank line
ccc     //7th line,

预期的输出文本:

aaa'\n' //1st line, there is a '\n' append to 'aaa'  
'\n'    //2nd line, blank line
bbb'\n' //3rd line, there is a '\n' append to 'bbb'
'\n'    //4th line, blank line
ccc     //5th line,

2) 如果是上面那个确切的程序目标,那么

if (c == '\n')
{
    newLine++;
    if (newLine < 3) // here should be 3 to print '\n' twice,
                     // one for 'aaa\n', one for blank line 
    {
        //printf("new line");
        putchar(c); 
    }
}

3) 如果你必须在Cygwin下处理Windows格式文件(以\r\n结尾),那么你可以这样做

while ((c = fgetc(fileContents)) != EOF)
{   
    if ( c == '\r') continue;// add this line to discard possible '\r'
    if (c == '\n')
    {
        newLine++;
        if (newLine < 3) //here should be 3 to print '\n' twice
        {
            printf("new line");
            putchar(c); 
        }
    }
    else 
    {
        putchar(c); 
        newLine = 0;
    }
}

【讨论】:

  • 嗨,是的,我的目标是摆脱任何领先的多条新线,并将连续的新线压缩成一条。但我无法让它工作。
  • 我尝试了您的代码,它在我的系统(OS X)上运行良好。它将预期结果(挤压'\n')输出到标准输出,而fileContents.txt 仍然是那些多余的'\n'。所以,请参考 Jonathan Leffler 的建议,考虑 Cygwin 和 Windows 的格式差异。
  • 您运行的是我的完整代码还是只运行了 sn-p?我通过添加 if (c=='\r'){ puts('CR\N') } 和之后添加 if(c=='\n') { newLine++; if (newLine &gt; 2) {putchar(c);} 尝试了他的建议,但所做的只是将 CR 添加到每第二个新行
  • 我将其更改为 if (c=='\'r || c == '\n') 现在它只是将所有内容压缩到一行这是我的代码pastebin.com/6fxfbM53
  • @nb023 除此之外,你也改成if(newLine &lt;2){ putchar('\n') }了吗?我如上所述更新了我的评论。
【解决方案3】:

[已编辑] 最小的变化是:

if ( newLine <= 2)

请原谅我忘记之前的代码。

一个稍微简单的替代方案:

int c;
int duplicates=0;
while ((c = fgetc(fileContents)) != EOF)
{
    if (c == '\n') {
        if (duplicates > 1) continue;
        duplicates++;
    }
    else {
        duplicates=0;
    }
    putchar(c);
}

【讨论】:

  • 您好,谢谢,我刚刚尝试了您建议的两种方法,但都不起作用。如果您想查看,我已将完整代码添加到原始帖子中的 pastebin 链接中。
【解决方案4】:

干跑代码: 如果文件以换行符开头并且newLine1

第一次迭代:

if (c == '\n') //Will be evaluated as true for a new-line character. 
{
    newLine++; //newLine becomes 2 before next if condition is evaluated.
    if (newLine < 2) //False, since newLine is not less than 2, but equal.
    {
        printf("new line");
        putchar(c); 
    }
}
else //Not entered
{
    putchar(c); 
    newLine = 0;
}

第二次迭代时:(假设是连续换行符大小写)

if (c == '\n') //Will be evaluated as true for a new-line character.
{
    newLine++; //newLine becomes 3 before next if condition is evaluated.
    if (newLine < 2) //False, since newLine is greater than 2.
    {
        printf("new line");
        putchar(c); 
    }
}
else //Not entered
{
    putchar(c); 
    newLine = 0;
}

所以,

  • newLine初始化为0

【讨论】:

  • 您好,谢谢,我刚刚尝试将其更改为 0,但仍然无法正常工作。如果您想查看,我已将完整代码添加到原始帖子中的 pastebin 链接中。
【解决方案5】:
if newline > 2     

如果你想去掉第二行,那应该大于或等于。 此外,您的换行符从一开始,然后递增到二,然后重置为零。相反,我建议用布尔值替换计数

boolean firstNewlineFound = false

然后每当您找到换行符时,将其设置为 true;只要为真,删除一个换行符并将其设置回假。

【讨论】:

    猜你喜欢
    • 2013-05-23
    • 2011-09-10
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    • 2016-11-20
    • 1970-01-01
    • 2017-10-20
    • 2017-11-08
    相关资源
    最近更新 更多