csv 在Perl中处理替换和拆分中的CRLF

4urapxun  于 2023-04-27  发布在  Perl
关注(0)|答案(1)|浏览(124)

我需要处理许多文件(以CRLF行结尾),看起来像这样:

$ cat -v file1.txt
1$XXX$ZZZ$$$$$$$$^M
2$AAA$BBB$$$$$$$$^M

$ cat -v file2.txt
1$4668$$$^M
2$46$$$^M

我需要:

  • 删除最后一个$符号,
  • 将所有$更改为,
  • 用双引号将每个字段括起来,
  • 重命名文件。

所需输出:

$ cat -v newname1.csv
"1","XXX","ZZZ","","","","","","",""^M
"2","AAA","BBB","","","","","","",""^M

$ cat -v newname2.csv
"1","4668","",""^M
"2","46","",""^M

以下是我的尝试:

#!/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 $!;
  
}

它给出了在最后一列中包含一个新行的文件:

$ cat -v newname1.csv
"1","XXX","ZZZ","","","","","","","","^M
"^M
"2","AAA","BBB","","","","","","","","^M
"^M

$ cat -v newname2.csv
"1","4668","","","^M
"^M
"2","46","","","^M
"^M

我猜这个问题是由于CRLF行结束。因此,我尝试:

  • '<'更改为'<:crlf'以打开我的文件,结果相同;
  • 使用其他正则表达式来匹配最后一个$符号(例如\$\r\n\$\R,它们都导致文件没有空的尾随列)。

我如何修复我的代码以获得我想要的输出?

mjqavswn

mjqavswn1#

更新:这个答案是为前两个版本的问题写的。我只是因为OP要求我删除它。它可能不适合当前版本的问题。有些事情可能是完全错误的。
这与行结束符是CRLF无关。这只是一个split问题。
如果我在代码中添加一个Dumper打印,其中您已拆分为变量@row

my @row = split /\$/;
use Data::Dumper;
print Dumper \@row;

我得到(对于第一个字段):

$VAR1 = [
          '1',
          '4668',
          '',
          '',
          '
'
        ];

在分割的最后一个字段中可以看到尾随的换行符。
然后,当您将这些拆分结果视为数据中的真正列值时,将为换行符添加1个字段。
我看不出你在哪里删除了最后一个$。也许这是你误解了什么?

建议方案:

如果这是csv数据,您应该使用csv模块来处理它。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);
}

更新:
自从你编辑了你的问题并添加了一行代码,改变了 * 一切 * 并使你自己声称的输出“错误”,我发现了以下内容:
如果您只有尾随的空字段,split将默认删除这些空字段。这可以修复,如documentation for split中所指定的:
如果LIMIT是负的,则它被视为任意大的;产生尽可能多的场。
如果LIMIT被省略(或者,等价地,零),则它通常被视为好像它是负的,但是例外的是尾部空字段被剥离(空的前导字段总是被保留);如果所有字段都是空的,则所有字段都被认为是拖尾的(并且因此在这种情况下被剥离)。
换句话说,你可以改变

split /\$/;

split /\$/, $_, -1;

以修复丢失的尾随空字段。
唯一的问题是你还没有报告有这个问题(还没有)。所以,我想我们需要等待你更新你的问题。

相关问题