Здравствуй кмьюнити!
Есть давлольно простая задача. Нужно распарсить строку вида (размер строки произвольный):
'name1' val1 'name2' val2 ...
все данные поместить в массив из анонимных массивов:
[
[name1, val1],
[name2, val2],
...
]
Сделать всё это нужно максимально быстрым способом (имеется ввиду время выполнения кода).
Сразу хочу отметить, пользоваться сампоисанными модулями использующие C, Xs запрещается, так же нельзя использовать нестандартные модули или строноннии программы.
Исходя из этих усолвий, на ум пришло несколько решений, но их скрорость не сильно удовлетворяет =( Вот собственно код этих решений:
#!/usr/bin/perl -w
use strict;
use Benchmark ':all';
my $str = "'xxx' zzz 'yyy' mmm 'sdfg' oelflv";
cmpthese (10000, { test0 => \&test0, test1 => \&test1, test2 => \&test2, test3 => \&test3 } );
sub test0 {
my $str_buff = $str;
my @result_list = ();
while($str_buff =~ m/'([a-zA-Z0-9]+)' ([a-zA-Z0-9]+)/g) {
push @result_list, [$1,$2];
}
return \@result_list;
}
sub test1 {
my @array = map {chr($_)} unpack('C*',$str);
my $length = scalar @array;
my $i;
my $name;
my $val;
my @result_list = ();
for($i=0;$i<$length;$i++) {
$i++;
$name = '';
$val = '';
for(;$i<$length;$i++) {
last if($array[$i] eq '\'');
$name .= $array[$i];
}
$i++;$i++;
for(;$i<$length;$i++) {
last if($array[$i] eq ' ');
$val .= $array[$i];
}
push @result_list, [$name,$val];
}
return \@result_list;
}
sub test2 {
my $exit_flag = 0;
my $index_old=0;
my $index_new;
my $name;
my $val;
my @result_list = ();
while(!$exit_flag) {
$index_old++;
$index_new = index($str, '\'', $index_old);
$name = substr($str,$index_old,$index_new-$index_old);
$index_old = $index_new;
$index_old +=2;
$index_new = index($str, ' ',$index_old);
if($index_new >= 0) {
$val = substr($str,$index_old,$index_new-$index_old);
$index_old = $index_new;
$index_old++;
} else {
$val = substr($str,$index_old,length($str)-$index_old);
$exit_flag = 1;
}
push @result_list, [$name,$val];
}
return \@result_list;
}
sub test3 {
my $string = $str;
my $index;
my $name;
my $val;
my @result_list = ();
while(length $string) {
substr($string,0,1) = '';
$index = index($string, '\'');
$name = substr($string,0,$index);
substr($string,0,$index+2) = '';
$index = index($string, ' ');
if($index >= 0) {
$val = substr($string,0,$index);
substr($string,0,$index+1) = '';
} else {
$val = $string;
$string = '';
}
push @result_list, [$name,$val];
}
return \@result_list;
}
В результате этого теста получается, что пока чемпион - тест номер 2:
Rate test1 test3 test0 test2
test1 271/s -- -76% -78% -81%
test3 1152/s 325% -- -7% -20%
test0 1238/s 357% 7% -- -14%
test2 1439/s 431% 25% 16% --
Ещё, был вариант, прикрутить кэширование, так как иногда строки могут повторятся.
Так же был вариант для коротких строк использовать один метод, а для более длинных другой (например, вриант из теста номер 1 для коротких, а вариант из теста номер 2 для более длинных), но в обоих категориях код теста номер 2, показал себя с лучшей стороны.
Возможно, как всегда и бывает, ваш свежий взгляд найдёт ещё лучшее решение, на что собственно я и надеюсь)
Всем заранее спасибо!