使用perl Text :: CSV_XS在CSV中修复换行符

时间:2018-12-17 13:43:21

标签: perl csv

我正在尝试清理一些没有转义的csv文件。

我没有perl经验,但是从Text :: CSV_XS的示例中刮了几行代码,但我设法得到了一个有效的脚本,除了未转义的换行符。

https://gist.github.com/samvdb/761d12cb6e0275105a689ce25765496d

#!/usr/bin/perl

# This script can be used as a base to parse unreliable CSV streams
# Modify to your own needs
#
#      (m)'08 [23 Apr 2008] Copyright H.M.Brand 2008-2018

use strict;
use warnings;

sub usage {
    my $err = shift and select STDERR;
    print <<"EOH";
usage: $0 [-o file] [-s S] [file]
    -o F  --out=F     output to file F (default STDOUT)
    -s S  --sep=S     set input separator to S (default ; , TAB or |)
EOH
    exit $err;
} # usage

use Getopt::Long qw(:config bundling);
GetOptions (
    "help|?"        => sub { usage (0); },
    "s|sep=s"       => \my $in_sep,
    "o|out=s"       => \my $opt_o,
    ) or usage (1);

use Text::CSV_XS qw( csv );

my $io  = shift || \*DATA;
my $eol = "\n";

binmode STDOUT, ":encoding(utf-8)";

my @hdr;
my @opt_i = (
    in  => $io,
    binary             => 1,
    blank_is_undef     => 1,
    allow_loose_quotes => 1,
    allow_loose_escapes => 1,
    sep => ";",
    encoding => "utf16le",
    );

my @opt_o = (out => \*STDOUT, eol => $eol, sep => ",", quo => '"',             always_quote  => 1,);


push @opt_i,
    bom          => 1,
    sep_set      => [ $in_sep ],
    keep_headers => \@hdr;
push @opt_o,
    headers      => \@hdr;

csv (in => csv (@opt_i), @opt_o);

__END__
a;b;c;d;e;f
"test"and also newline\nhere or something";2;3;4;5;6
"this happens also! "\n here or something";2;3;4;5;6
2;3;4;5;6;7
3;4;5;6;7;8
4;5;6;7;8;9

示例输入:

a;b;c;d;e;f
"test"and also newline\nhere or something";2;3;4;5;6
"this happens also! "\n here or something";2;3;4;5;6
2;3;4;5;6;7
3;4;5;6;7;8
4;5;6;7;8;9

该行的预期结果:

"test""and also newline<br/>here or something";2;3;4;5;6
"this happens also! ""<br/> here or something";2;3;4;5;6

有人可以帮我修复此Perl脚本,以便将\ n替换为
吗?

谢谢

2 个答案:

答案 0 :(得分:1)

如果您的分隔符(';')不需要转义,并且行中的列数是恒定的,则可以不用Text::CSV来解析数据。然后,您可以根据需要进行清理。但是,您将需要了解一些Perl以根据您的特定需求清理单元。

use strict;
use warnings;

# slurp file into a string and split it
open my $fh,'<',$ARGV[0];
$/ = undef;
my @data = split ';', <$fh>;

my $columns = 6;
my @new_data;

# splice 6 elements from the array at a time until the array is out of elements
while (@data) {
    my @row = splice @data, 0, $columns;
    for my $cell (@row) {
        # inspect / clean up $cell 
    }
    push @new_data, \@row; 
}

for my $row (@new_data) {
    print join(';', @$row)."\n"; 
}

并不是说这会保留$cell中的所有换行符,包括每一行的末尾。

答案 1 :(得分:0)

您的示例输入看起来像格式不正确的CSV格式-我认为您列出的内容无法解析为正确的CSV。例如:

"test"and also newline\nhere or something";2;3;4;5;6
"this happens also! "\n here or something";2;3;4;5;6

数据周围的“引号”表示其中包含的所有内容可能都有特殊字符(定界符,换行符等),但是当您在此处关闭引号时:

"test"and also newline\nhere or something";2;3;4;5;6
     ^

你打破了。要嵌入报价,您需要添加两个引号。格式正确:

"test""and also newline\nhere or something";2;3;4;5;6

假设实际(渲染的)文本为test" and also...

如果我了解您要做什么,请用HTML换行符替换换行符,我认为这可以解决问题:

use Text::CSV_XS qw(csv);

my @rows;

my $csv = Text::CSV_XS->new({
  binary => 1,
  auto_diag => 1,
  sep_char => ';'
});

open my $IN, '<:encoding(utf8)', "test.csv" or die;
open my $OUT, '>:encoding(utf8)', "new.csv" or die;
while (my $row = $csv->getline($IN)) {
  s/\n/<br>/g for @$row;
  $csv->print ($OUT, $row);
  print $OUT "\n";
}
close $OUT;
close $IN;

如果这是示例输入:

a;b;c;d;e;f
"test""ja ze";2;3;4;5;6
2;3;"This Text has
a newline";5;6;7
3;4;5;6;7;8
4;5;6;7;8;9

这将是输出:

a;b;c;d;e;f
"test""ja ze";2;3;4;5;6
2;3;"This Text has<br>a newline";5;6;7
3;4;5;6;7;8
4;5;6;7;8;9

但同样,这些都假设格式正确的CSV数据。