我发现自己在大约8年内第一次写PERL而且我遇到了一些应该很容易的事情。这是基本前提:
包含一百个左右字段的文件,其中10个数据的数据不正确(O为0)
A B C D E F ...
br0wn red 1278076 0range "20 tr0ut" 123 ...
Green 0range 90876 Yell0w "18 Salm0n" 456 ...
我正在尝试编写程序来拆分字段然后允许我在字段A上运行正则表达式用0替换0但不用0替换0用于列C等等我还有另外需要的问题例如,可能为列E运行备用正则表达式。
我能够通过/ t拆分记录中的所有字段。我有一个问题格式化我的命令来遍历每个字段并根据字段运行特定的正则表达式。
任何帮助都将不胜感激,如果你解决了,我会以10美元的价格为你选择的饮料付款。
答案 0 :(得分:1)
使用诸如Text::CSV
之类的csv解析器并不复杂。这样的事情就足够了:
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV->new({
sep_char => "\t",
binary => 1,
eol => $/,
});
while (my $row = $csv->getline(*DATA)) {
tr/0/o/ for @{$row}[0, 1, 3]; # replace in cols A, B and D
s/(?<!\d)0(?!\d)/o/g for @{$row}[4]; # replace in col E
$csv->print(*STDOUT, $row); # print the result
}
__DATA__
A B C D E F
br0wn red 1278076 0range "20 tr0ut" 123
Green 0range 90876 Yell0w "18 Salm0n" 456
<强>输出:强>
A B C D E F
brown red 1278076 orange "20 trout" 123
Green orange 90876 Yellow "18 Salmon" 456
请注意,我使用简单的正则表达式而不是音译(全局替换)来处理混合字符串(列E),并且它不会替换数字旁边的零,这对于某些数字会失败,例如{{ 1}}或20.0
。
<强>更新强>
如果你想根据列名称而不是位置进行替换,事情会变得复杂一些。但是,0
可以处理它。
Text::CSV
此代码是独立的演示代码。要在文件上尝试代码,请将use strict;
use warnings;
use Text::CSV;
my @pure_text = qw(A B D);
my @mixed = qw(E);
my $csv = Text::CSV->new({
sep_char => "\t",
binary => 1,
eol => $/,
});
my $cols = $csv->getline(*DATA); # read column names
$csv->print(*STDOUT, $cols);
$csv->column_names($cols); # set column names
while (my $row = $csv->getline_hr(*DATA)) { # hash ref instead of array ref
tr/0/o/ for @{$row}{@pure_text}; # substitution on hash slice
s/(?<!\d)0(?!\d)/o/g for @{$row}{@mixed};
my @row = @{$row}{@$cols}; # make temp array for printing
$csv->print(*STDOUT, \@row);
}
__DATA__
A B C D E F
br0wn red 1278076 0range "20 tr0ut" 123
Green 0range 90876 Yell0w "18 Salm0n" 456
更改为*DATA
并使用以下脚本:
*STDIN
答案 1 :(得分:0)
这是使用GNU awk
的一种方式。只需将列名添加到BEGIN
块中的数组中即可。在下面的示例中,仅修改A,C和E列。像:
awk -f script.awk file
script.awk
的内容:
BEGIN {
FS=OFS="\t"
a["A"]
a["C"]
a["E"]
}
{
for (i=1;i<=NF;i++) {
if ($i in a && NR==1) {
b[i]
}
else if (i in b) {
$i = gensub(/(^|[^0-9])0([^0-9]|$)/,"\\1o\\2", "g", $i)
}
}
}1
制表符分隔结果:
A B C D E F ...
brown red 1278076 0range "20 trout" 123 ...
Green 0range 90876 Yell0w "18 Salmon" 456 ...
或者,这是单行:
awk 'BEGIN { FS=OFS="\t"; a["A"]; a["C"]; a["E"] } { for (i=1;i<=NF;i++) { if ($i in a && NR==1) b[i]; else if (i in b) $i = gensub(/(^|[^0-9])0([^0-9]|$)/,"\\1o\\2", "g", $i) } }1' file
答案 2 :(得分:0)
创建一个子例程数组,如:
my @fixer;
$fixer[0] = sub { $_[0] =~ s/0/o/; };
my @fields = split /\t/, $input;
for (my $i = 0; $i <= $#fields; $i++) {
$fixer[$i]->($fields[$i]) if defined $fixer[$i];
}
答案 3 :(得分:0)
我可能会在'autosplit'模式下使用Perl:
perl -a -p -F"\t" \
-e '$F[0] =~ s/0/o/g;
$F[1] =~ s/0/O/g;
$F[3] =~ s/0/o/g;
$F[4] =~ s/(\D)0(\D)/\1o\2/g; # Or other more complex regex
# ... # Other fields can be edited
$_ = join("\t", @F); # Reassign fields to $_
' data-file
$F[4]
的正则表达式'20'变成'20鳟鱼';如果需要,你可以使它更复杂。
样本数据输出:
A B C D E F ...
brown red 1278076 orange "20 trout" 123 ...
Green Orange 90876 Yellow "18 Salmon" 456 ...
这确实假设一个严格的制表符分隔的数据文件。如果您没有严格的制表符分隔数据,则包含空格的带引号的字符串会使事情复杂化;在这一点上,Text :: CSV对于阅读这些行很有吸引力。
答案 4 :(得分:0)
这是使用数组引用和/或子例程进行简单配置的一种方法,然后稍后进行替换:
use strict;
use warnings;
my @subst = ([
['this', 'that'],
['O', 1],
],[
['foo', 'boo'],
sub {s/a.*//},
]);
sub mk_subst {
my $list = shift;
my ($this, $that) = eval { @$list };
return $list unless defined $this;
sub { s/\Q$this/$that/ };
}
my @all;
for my $set (@subst) {
my @list = eval { @$set };
unless (@list) {
push @all, [ sub {} ];
next;
}
my @re;
for my $s (@list) {
push @re, mk_subst($s);
}
push @all, \@re;
}
while (<DATA>) {
chomp;
my @list = split /\t/, $_, -1;
for my $i (0..$#list) {
for ($list[$i]) {
for my $funcs ($all[$i]) {
for my $f (@$funcs) {
$f->();
}
}
}
}
print join("\t", @list), "\n";
}
__DATA__
thisO fooabca1234
abc 123fooabca1234
答案 5 :(得分:0)
perl -F -lane 'for(@F){$_=~s/0/o/g if(/0/ && /[a-zA-Z]+/);} print "@F"' your_file
在下面测试
> cat temp
br0wn red 1278076 0range "20 tr0ut" 123 ...
Green 0range 90876 Yell0w "18 Salm0n" 456 ...
> perl -F -lane 'for(@F){$_=~s/0/o/g if(/0/ && /[a-zA-Z]+/);} print "@F"' temp
brown red 1278076 orange "20 trout" 123 ...
Green orange 90876 Yellow "18 Salmon" 456 ...
>