Perl sub优化使用split将字符串推入csv

时间:2010-06-25 23:12:05

标签: perl

我想优化这个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并返回字符串需要时间,我只想到一种加速它的方法,我连续有多个子调用。拆分一次,然后将多个东西推入数组,然后在最后加入一次。

5 个答案:

答案 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
  • ysth's suggestion基本相同
  • 仅根据需要执行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几百万次遇到瓶颈......那么请不要splitjoin。每行最初进入系统时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///是个好主意。

相关问题