【问题标题】:Text Lines are missed when reading a file Line by Line in Perl. <cr> <lf> mismatch在 Perl 中逐行读取文件时会丢失文本行。 <cr> <lf> 不匹配
【发布时间】:2013-03-01 01:51:41
【问题描述】:

我想从 3G 调制解调器中提取和记录各种参数,因为存在间歇性丢失。因此,我使用 wget 从 3G 调制解调器读取3Ginfo.html 并将内容放入文件contents.txt。使用 Notepad++ 打开这个文件会显示所有的数据。

由于我的声誉,我不能发布图片,因此下面的代码是我能做的最好的;从记事本++(打开查看所有字符),我得到:

<tr>[LF]

<td class='hd'>Signal Strength:</td>[LF]

<td>[LF]

-72[CR]

&nbsp(dBm)&nbsp(High)</td>[LF]

</tr>[LF]

但是,当从 Perl 逐行读取文件时,很明显行数少于 Notepad++ 报告的行数,并且数据丢失。在这种情况下,实际的信号强度值会丢失。

这里是读取文件的 Perl 代码:

open hLOGFILE, "<output.txt";
while (<hLOGFILE>) 
{ 
    print "Line no $.  Text is $_ ";
}

这是输出(作为文本,因为我还不能发布图片):

Line no 98  Text is <tr>

Line no 99  Text is <td class='hd'>Signal Strength:</td>

Line no 100  Text is <td>

&nbsp(dBm)&nbsp(High)</td>

Line no 102  Text is </tr>

很明显,缺少行,它与&lt;cr&gt; 行尾终止符有关。我试过 slurping 文件,但仍然缺少这些行。

除了逐字节读取然后尝试以这种方式解析文件(这不是很吸引人)之外,我找不到解决方案。

我的计划是每分钟左右简单地提取和记录感兴趣的行。

我尝试打开指定各种编码的文件,但仍然没有乐趣。如果 Notepad++ 可以读取并显示所有数据,为什么它在 Perl 中不起作用。在 Windows XP 命令行中使用more 时,显示数据也丢失了。

当我从 chrome 查看源代码时,

<tr>
    <td class='hd'>Received Signal Code Power(RSCP):</td>
    <td align='center'> -78 dBm</td>
</tr>

【问题讨论】:

    标签: perl eol


    【解决方案1】:

    -72[CR] 行没有丢失。你只是没有看到它。

    这是因为它不是 ,因为回车字符通常不被识别为换行符。发生的事情是您将其作为一行阅读:

    -72[CR]&nbsp(dBm)&nbsp(High)</td>[LF]
    

    正在发生的事情是您正在打印:

    Line No. 101 is -72
    

    然后打印回车符,使光标回到行首。然后,打印该行的其余部分。这掩盖了您打印的内容,因此您会看到:

    &nbsp(High)</td>
    

    因为它覆盖了该行上的先前文本。

    我用 VI 创建了三个不同的文件,具有三种不同的文件格式(“mac”=“\r”、“unix”=“\n”和“dos”=“\r\n”),然后我使用 Unix cat 命令将它们组合成一个混蛋文件。

    这是我的程序:

    use 5.12.0;
    use autodie;
    
    open my $test_fh, "<:crlf", "new_test";
    
    local ($/);               #Enable "slurp" mode
    my $file = <$test_fh>;    #Whole file is read in.
    
    $file =~ s/[\r\n]+/\n/g;  #Make all line endings just \n
    
    #
    # Now "rewrite" the file
    #
    my @file = split /\n/, $file;
    for my $line (@file) {
        say qq(Line: "$line");
    }
    

    打印出来:

    Line: "MAC FILE"
    Line: "this"
    Line: "is"
    Line: "a"
    Line: "test of my"
    Line: "program"
    Line: "this"
    Line: "WINDOWS FILE"
    Line: "is"
    Line: "a"
    Line: "test of my"
    Line: "program"
    Line: "UNIX FILE"
    Line: "this"
    Line: "is"
    Line: "a"
    Line: "test of my"
    Line: "program"
    

    如您所见,MAC FILE 确实显示了所有行,但单词Line: 并没有全部打印出来。那是因为 Perl 把它读成了一大行。我的s/\r+/\n/g 将其转换为多行打印,但while 循环将其作为单行读取。

    看看我的open 声明。我使用三个参数来解决 Perl 中的一些小问题。好处是您可以将图层或编码附加到文件中。例如,&lt;:crlf 自动将 Windows 文件从 \r\n 结尾转换为 \n,但不会触及 Unix 文件。对于在混合 Unix/Windows 环境中工作的人来说,这是一个救命稻草。

    我希望为旧的 Mac 样式文本文件找到一些类似的层(在 Mac OS X 之前的日子里,Macintosh 文件仅以 \r 结尾,而根本没有 \n。那真的可以解决问题。不幸的是,我没有找到任何文档。很久没有你有 pre-OS X Macintosh 文本文件了。

    【讨论】:

    • 太棒了,谢谢。这就是问题
    【解决方案2】:

    回车是\r。它在perldoc perlreref 中列出。从您的输入中删除它,例如在您的那个循环中,可以这样做:

    while (<hLOGFILE>) { 
        s/\r//g;
        print "Line no $.  Text is $_ ";
    }
    

    替代品

    tr/\r//d;        # same thing as above, really
    s/[\r\n]+$//;    # remove all line endings
    

    【讨论】:

      【解决方案3】:

      你可以 chomp() 它关闭...

      open hLOGFILE, "<output.txt";
      while (<hLOGFILE>)
      {
          chomp(); 
          print "Line no $.  Text is $_ \n" if( $_ );
      }
      

      在某些系统上,我看到需要调用 chomp() 两次,以消除多个行尾字符……是的,确实存在。您可能还想添加一些东西来去除所有这些 HTML 标记?见:How can I strip HTML in a string using Perl?

      【讨论】:

      • chomp() 和多个 chomp() 在这种情况下似乎没有帮助。谢谢
      猜你喜欢
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-06
      • 2015-05-12
      相关资源
      最近更新 更多