我的问题是双重的:
场景:我正在生成一个使用标准逗号分隔符的CSV日志文件。 CSV文件中目前有四个“列”,但将来可能会添加更多列。我有兴趣将我的REGEX搜索/替换限制为特定的数据列(例如,COL 2)。
第二列中的数据因响应设备而异。最终,我使用REGEX将值“调整”为浮点数到两位小数。一些条目带有各种工件(例如额外的字母或其他表示),我使用REGEX将其转换为适当的浮点表示。所有的REGEX目前都适用于我的目的,但是我想将这些REGEX仅限于第二列(COL 2)中的数据,所以如果我将来添加类似格式的列(例如,浮点数),它们就是不受第2栏的影响。
我想到的一种方法是使用拆分并将列数据保存到标量变量中,然后通过包含REGEX的循环或子程序运行COL2标量,然后将条件化的COL2值重新写回到a新的CSV文件。 (我现在用Perl的$ ^ I变量做这个。)
$^I = ".org";
while (<>) {
my ($col1, $col2, $col3, $col4) = (split /,/);
$col2 =~ s/EXP1/FORMATTED/;
$col2 =~ s/EXP2/FORMATTED/;
$col2 =~ s/EXP3/FORMATTED/;
my $new_rec = join ",", $col1,$col2,$col3,$col4;
print $new_rec;
}
问题1:我想知道这是否足够有效还是我做了不必要的工作?我可以在REGEX中专门添加逗号,使其仅对COL2有效吗?
问题2:回答问题re:split(SPLIT Question),brian d foy对Text :: CSV_XS说了以下内容:“它非常快,这就是我说”极度优化“的原因。它的文档。它也正确地处理CSV,而不是。“
如果这是准确的,意味着拆分不能正确处理CSV文件,我的上述解决方案是否会随着时间的推移而发挥作用?我不确定split的含义是不能正确处理CSV。
答案 0 :(得分:1)
回应toolic提到的内容,仅split
CSV数据存在风险,因为您可能错误地split
列值。鉴于此,这是一个Text::CSV_XS选项:
use strict;
use warnings;
use Text::CSV_XS;
my $csv = Text::CSV_XS->new( { binary => 1, auto_diag => 1 } )
or die "Cannot use CSV: " . Text::CSV->error_diag();
my $sepChar = $csv->sep_char();
open my $fh, "<:encoding(utf8)", $ARGV[0] or die "$ARGV[0]: $!";
while ( my $row = $csv->getline($fh) ) {
$row->[2] =~ s/this/that/;
print join ',', map { /$sepChar/ ? qq{"$_"} : $_ } @$row;
}
$csv->eof or $csv->error_diag();
close $fh;
命令行用法:>perl script.pl inFile [>outFile]
最后一个可选参数将输出定向到文件。
请注意,您可以在第2列上执行所有s /// map
包括检查数组元素中的分隔符(通常是逗号)。如果存在,则元素用双引号括起来 - 以保持CSV格式。
希望这有帮助!
修改强>
由于您确定CSV字段中没有逗号,因此您实际上不需要split
数据,进行替换,重新组合数据,然后print
它。您可以设置一个哈希,其键/值对是替换中使用的匹配/替换对。然后,只需使用正则表达式捕获替换的col2值:
use strict;
use warnings;
my %hash = ( 1 => '1.00', 'unk' => '0.00' );
while (<DATA>) {
s/^(?:.+?,)\K([^,]+)/exists $hash{$1} ? $hash{$1} : $1/e;
print;
}
__DATA__
12345,1,342,789.0
47.42,unk,17.6,12
17,34,12.5,0
输出:
12345,1.00,342,789.0
47.42,0.00,17.6,12
17,34,12.5,0
答案 1 :(得分:0)
这取决于您的数据来自何处。如果您某些该字段永远不会出现在引号中,那么split
就可以了,并且是最佳解决方案。
以下是我编写代码的方法。对于块的范围,for
循环暂时将$_
别名为$fields[1]
,并允许您在不明确提及变量的情况下操纵该值。输出将发送到STDOUT
。
use strict;
use warnings;
while (<>) {
chomp;
my @fields = split /,/;
for ($fields[1]) {
s/EXP1/FORMATTED/;
s/EXP2/FORMATTED/;
s/EXP3/FORMATTED/;
}
print join(',', @fields), "\n";
}