我需要处理许多看起来像这样的文件(带有
CRLF
行尾):
$ cat -v file1.txt
1$XXX$ZZZ$$$$$$$$^M
2$AAA$BBB$$$$$$$$^M
$ cat -v file2.txt
1$4668$$$^M
2$46$$$^M
我需要:
$
标志,$
更改为,
,期望的输出(无论行尾是
CRLF
还是LF
):
$ cat newname1.csv
"1","XXX","ZZZ","","","","","","",""
"2","AAA","BBB","","","","","","",""
$ cat newname2.csv
"1","4668","",""
"2","46","",""
这是我的尝试:
#!/usr/bin/perl
use strict;
use warnings;
my %inputs = qw(
file1 file1.txt
file2 file2.txt
);
my %outputs = qw(
file1 newname1.csv
file2 newname2.csv
);
for my $key (keys %inputs) {
open my $in, '<', $inputs{$key} or die $!;
open my $out, '>', $outputs{$key} or die $!;
while(<$in>) {
local $, = ',';
local $\ = "\n";
s/\$$//;
my @row = split /\$/;
print $out map qq("$_"), @row;
}
close $in or die $!;
close $out or die $!;
}
在 Linux 上,它给出的文件在最后一列中包含
CRLF
和 LF
行结尾:
$ cat -v newname1.csv
"1","XXX","ZZZ","","","","","","","","^M
"
"2","AAA","BBB","","","","","","","","^M
"
$ cat -v newname2.csv
"1","4668","","","^M
"
"2","46","","","^M
"
我想这个问题是由于
CRLF
行尾。因此,我尝试了:
'<'
更改为 '<:crlf'
以打开我的文件,结果相同;$
符号(例如\$\r\n
和\$\R
,它们都会导致文件没有空尾随列)。如何修复我的代码以获得我想要的输出?
更新:这个答案是为问题的前两个版本写的。我只是取消删除它,因为 OP 要求我这样做。它可能不适合当前版本的问题。有些事情可能是完全错误的。
split
问题。
如果我将 Dumper 打印添加到您的代码中,您已将其拆分为变量
@row
my @row = split /\$/;
use Data::Dumper;
print Dumper \@row;
我得到(第一个字段):
$VAR1 = [
'1',
'4668',
'',
'',
'
'
];
您可以看到尾随换行符的地方是拆分中的最后一个字段。
然后当您将这些拆分结果视为数据中的真实列值时,您会为换行符添加 1 个字段。
我看不到你在哪里删除最后一个
$
。也许这是你误解了?
建议的解决方案:
Text::CSV
模块在这方面做得很好。这是将处理您的输入的示例代码:
use strict;
use warnings;
use Text::CSV qw(csv);
my %inputs = qw(
file1 file1.txt
file2 file2.txt
);
my %outputs = qw(
file1 newname1.csv
file2 newname2.csv
);
for my $key (keys %inputs) {
my $aoa = csv (in => $inputs{$key}, sep_char => '$');
csv (in => $aoa, out => $outputs{$key}, sep_char => ',', always_quote => 1);
}
更新:
由于您编辑了问题并添加了一行代码来更改everything并使您自己声称的输出“错误”,我发现以下内容:
split
将默认删除这些空字段。这可以修复,如 documentation for split 中所指定:
如果 LIMIT 为负,则将其视为任意值 大的;产生尽可能多的字段。
如果 LIMIT 被省略(或等效地为零),那么它通常是 被视为负面的,但除了 尾随空字段被剥离(空前导字段总是 保留);如果所有字段都为空,则所有字段都被认为是 尾随(因此在这种情况下被剥离)。
换句话说,你可以改变
split /\$/;
到
split /\$/, $_, -1;
修复丢失的尾随空白字段。
唯一的问题是你还没有报告有这个问题(还)。所以,我想我们需要等你更新你的问题。
注意:此答案的目的是为未来的读者澄清问题和一些解决方案(由于我对问题的不合时宜的编辑)。信用应该转到@TLP answer。此外,正如他所建议的,正确的解决方案可能是使用
Text::CSV
模块,但破译这个问题是有教育意义的。
代码中有两个问题:
\$\r\n
或 \$\R
匹配最后一个 $
符号时,它按预期工作。但是,由于最后一列是空的(即没有CRLF
包含在最后一列中),split
默认删除它们.\$$
匹配最后一个$
符号时,一个CRLF
被包含在最后一列中(我不明白为什么);在 Linux 上,一个可能的解决方法是:
split
更改为 LIMIT
来指定 split /\$/;
的 split /\$/, $_, -1;
论点(以解决问题 1);$
符号从\$$
到\$\r\n
(或\$\R
); (以下简称myscript_fix1.pl
)local $/ = "\r\n"; chomp;
循环的开头添加while
并保持正则表达式\$$
匹配最后一个$
; (以下简称myscript_fix2.pl
)$
符号(即删除s/\$$//;
)并在pop @row;
之后添加split
。 (以下简称myscript_fix3.pl
)在 Windows 上,需要进行一些调整。
我机器上的一些时间使用
file1.txt
和 file2.txt
重复 10000 次:
$ time myscript_fix1.pl
real 0m0,199s
user 0m0,179s
sys 0m0,018s
$ time myscript_fix2.pl
real 0m0,234s
user 0m0,215s
sys 0m0,017s
$ time myscript_fix3.pl
real 0m0,176s
user 0m0,159s
sys 0m0,016s