【问题标题】:Different behaviors of reading from files generated on different machines从不同机器上生成的文件中读取的不同行为
【发布时间】:2013-10-10 09:26:22
【问题描述】:

我有一个包含数百个文本文件的文件夹。每个文件都有相同的格式,例如名称为ATextFile1.txt 的文件读取

ATextFile1.txt  09 Oct 2013
1
2
3
4
...

我有一个简化的 Perl 脚本,它应该读取文件并在终端窗口中打印出来:

#!/usr/bin/Perl

use warnings;
use strict;

my $fileName = shift(@ARGV);

open(my $INFILE, "<:encoding(UTF-8)", $fileName) || die("Cannot open $fileName: $!.\n");

foreach (<$INFILE>){
   print("$_");  # Uses the newline character from the file
}

当我在由生成ATextFile1.txt 的 Windows 版本的程序生成的文件上使用此脚本时,我的输出完全符合我的预期(作为文本文件的内容),但是,当我运行此Mac版文件生成程序生成的文件的脚本,输出如下:

2016tFile1.txt  09 Oct 2013

经过一些测试,它似乎只打印文本的第一行,其中前 4 个字符被正则表达式中可以表示为 /[0-9][0-9]16/ 的内容覆盖。如果在我的 Perl 脚本中,我将输出语句替换为 print("\t$_");,我会在 STDOUT 中打印以下行:

2016    ATextFile1.txt  09 Oct 2013

这些文件中的每一个都可以使用任何标准文本编辑器正常读取,但由于某种原因,我的 Perl 脚本似乎无法正确读取和写入文件。任何帮助将不胜感激(我希望我很明显错过了一些东西)。提前致谢!

【问题讨论】:

  • use Data::Dumper; $Data::Dumper::Useqq=1; print Dumper $_;
  • 这似乎是朝着正确方向迈出的一步。我得到我的输出类型有什么原因吗?我真的需要开始阅读中级 Perl,因为我知道 Data::Dumper 很早就讨论过了。

标签: perl readfile


【解决方案1】:

请注意,如果您将 UTF-8 字符打印到 STDOUT,则需要使用

binmode STDOUT, ':encoding(utf8)';

事先。

您的 Mac 文件看起来好像只有 CR 作为行尾。我知道最近版本的 Macintosh 系统使用 LF 作为行尾(与 Linux 相同),但 Mac OS 9 仅使用 CR,而 Windows 使用文件中的两个字符 CR LF,PerlIO 层将其转换为仅 LF当 perl 在 Windows 平台上运行时。

如果文件中没有换行符,那么 Perl 会将整个文件作为一条记录读取,打印时会将所有行叠加在一起。

只要文件相对较小,使用相同 Perl 代码读取任一文件格式的最简单方法是读取整个文件并将其拆分为 CR 或 LF。根据输入文件的来源,其他任何内容都需要不同的代码。

试试这个版本的代码。

use strict;
use warnings;

my @contents = do {
  open my $fh, '<:encoding(utf8)', $ARGV[0];
  local $/;
  my $contents = <$fh>;
  split /[\r\n]+/, $contents;
}

print "$_\n" for @contents;

更新

您可以尝试的另一种方法是使用PerlIO::eol 模块,该模块提供了一个PerlIO 层,当读取记录时,该层将任何 行结尾转换为LF。我不确定它是否与 UTF-8 搭配得很好,但只要将它添加到 encoding 层之后就可以了。

它不是核心模块,所以你可能需要安装它,但之后程序就变成了

use strict;
use warnings;

open my $fh, '<:encoding(UTF-8):eol(LF)', $ARGV[0];
binmode STDOUT, ':encoding(utf8)';

print while <$fh>;

我已经创建了 Windows、Linux 和 Mac 风格的文本文件,并且该程序在所有这些文件中都可以正常工作,但是我无法检查具有 0x0D 或 0x0A 作为其编码一部分的 UTF-8 字符是否是正确通过,所以要小心。

更新 2

经过简单思考后,除了这些字符本身之外,当然没有包含 CR 或 LF 的 UTF-8 编码。 ASCII 范围之外的所有字符仅包含设置了最高位的字节,因此它们超过0x80,并且永远不能是0x0D0x0A

【讨论】:

  • 经过一些修改,您的方法运行良好。谢谢!
  • 出于好奇,您能否解释一下打印出来的数字是什么意思(即 2016 年)或它来自哪里?
  • 这可能是您数据文件的最后一行。当 CR 发送到终端时,后续字符只是放在同一行的开头,而不是应该放在下一行的开头。因此,如果您打印ATextFile1.txt 09 Oct 2013,然后打印CR2016,则原始行的ATex 将被数字覆盖,并且在整个文件输出后剩下的就是最长的文本行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-17
  • 2013-02-06
  • 2011-05-23
  • 1970-01-01
  • 2020-06-09
  • 2020-02-12
  • 1970-01-01
相关资源
最近更新 更多