我想优化这个Perl子:
push_csv($string,$addthis,$position);
用于将字符串放入CSV字符串中。
e.g。如果$string="one,two,,four"; $addthis="three"; $position=2;
然后push_csv($string,$addthis,$position)
将更改$string = "one,two,three,four";
sub push_csv {
my @fields = split /,/, $_[0]; # split original string by commas;
$_[1] =~ s/,//g; # remove commas in $addthis
$fields[$_[2]] = $_[1]; # put the $addthis string into
# the array position $position.
$_[0] = join ",", @fields; # join the array with commas back
# into the string.
}
这是我的代码中的瓶颈,因为它需要被称为几百万次。
如果你精通Perl,你能看看它,并提出优化/替代方案吗?提前致谢! :)
编辑: 转换为@fields并返回字符串需要时间,我只想到一种加速它的方法,我连续有多个子调用。拆分一次,然后将多个东西推入数组,然后在最后加入一次。
答案 0 :(得分:3)
由于多种原因,您应该使用Text::CSV来处理这些低级别的CSV详细信息。如果您能够安装XS版本,我的理解是它将比纯Perl中的任何操作都快。此外,该模块将正确处理您可能错过的各种边缘情况。
use Text::CSV;
my $csv = Text::CSV->new;
my $line = 'foo,,fubb';
$csv->parse($line);
my @fields = $csv->fields;
$fields[1] = 'bar';
$csv->combine(@fields);
print $csv->string; # foo,bar,fubb
答案 1 :(得分:2)
首先将数组保存为数组,而不是分隔字符串?
您可能需要查看Data :: Locations。
或尝试(未经测试,未经过标记,不会添加像您原来那样的新字段......)
sub push_csv {
$_[1] =~ y/,//d;
$_[0] =~ s/^(?:[^,]*,){$_[2]}\K[^,]*/$_[1]/;
return;
}
答案 2 :(得分:1)
一些建议:
tr/,//d
代替s/,//g
,因为它更快。这与使用y/,//d
split
。如果$position = 1
,并且您有10个字段,那么您在浪费计算执行不必要的拆分和连接。 split
的可选第三个参数可以在此处利用。 但是,这取决于您期望的连续空字段数。如果您事先不知道有多少这些 你真的应该使用Text::CSV
,但是这里我将如何修改纯Perl中sub的实现(假设最多连续一个空字段):
sub push_csv {
my ( $items, $positions ) = @_[1..2];
# Test inputs
warn "No. of items to add & positions not equal"
and
return unless @{$items} == @{$positions};
my $maxPos; # Find the maximum position number
for my $position ( @{$positions} ) {
$maxPos ||= $position;
$maxPos = $position if $maxPos < $position;
}
my @fields = split /,/ , $_[0], $maxPos+2; # Split only as much as needed
splice ( @fields, $positions->[$_], 1, $items->[$_] ) for 0 .. $#{$items};
$_[0] = join ',' , @fields;
print $_[0],"\n";
}
use strict;
use warnings;
my $csvString = 'one,two,,four,,six';
my @missing = ( 'three', 'five' );
my @positions = ( 2, 4 );
push_csv ( $csvString, \@missing, \@positions );
print $csvString; # Prints 'one,two,three,four,five,six'
答案 3 :(得分:1)
如果您在split
ting和join
几百万次遇到瓶颈......那么请不要split
和join
。每行最初进入系统时split
一次,在进行处理时传递该数组(或者更可能是对数组的引用),然后执行一次join
将其转换为当你准备离开系统时,一个字符串。
e.g:
#!/usr/bin/env perl
use strict;
use warnings;
# Start with some CSV-ish data
my $initial_data = 'foo,bar,baz';
# Split it into an arrayref
my $data = [ split /,/, $initial_data ];
for (1 .. 1_000_000) {
# Pointless call to push_csv, just to make it run
push_csv($data, $_, $_ % 3);
}
# Turn it back into a string and display it
my $final_data = join ',', @$data;
print "Result: $final_data\n";
sub push_csv {
my ($data_ref, $value, $position) = @_;
$$data_ref[$position] = $value;
# Alternately:
# $data_ref->[$position] = $value;
}
请注意,这简化了push_csv
成为单个,相当简单的处理线的事情,因此您可能只想进行内联更改而不是为其调用子,尤其是在运行时效率为关键标准 - 在这个简单的例子中,摆脱push_csv
并在内联中减少运行时间约70%(从0.515s到0.167s)。
答案 4 :(得分:0)
你不认为使用数组和splice
可能更容易,并且最后只使用join
来创建逗号分隔吗?
如果这是您代码中的主要瓶颈,我真的不认为反复使用s///
是个好主意。