Как я могу разделить строку Perl только в последнем появлении разделителя? - PullRequest
13 голосов
/ 18 марта 2010

my $ str = "1: 2: 3: 4: 5"; my ($ a, $ b) = split (':', $ str, 2);

В приведенном выше коде я использовал limit как 2, поэтому $ a будет содержать 1, а остальные элементы будут в $ b Таким образом, я хочу, чтобы последний элемент был в одной переменной, а элементы перед последним элементом должны быть в другой переменной.

Пример

$str = "1:2:3:4:5" ; 
# $a should have "1:2:3:4"  and $b should have "5" 
$str =  "2:3:4:5:3:2:5:5:3:2" 
# $a should have "2:3:4:5:3:2:5:5:3" and $b should have "2"

Ответы [ 6 ]

18 голосов
/ 18 марта 2010
split(/:([^:]+)$/, $str)
8 голосов
/ 18 марта 2010

Вы можете использовать шаблон , соответствующий вместо split():

my ($a, $b) = $str =~ /(.*):(.*)/;

Первая группа захватывает все до последнего появления ':', а вторая - все остальные.

В случае, если ':' - это , а не , присутствующий в строке, Perl достаточно умен, чтобы обнаружить это и провалить совпадение без возврата.

8 голосов
/ 18 марта 2010

вы также можете использовать rindex() например

my $str="1:2:3:4:5";
$i=rindex($str,":");
$a=substr($str,0,$i);
$b=substr($str,$i+1);
print "\$a:$a, \$b: $b\n";

выход

$ perl perl.pl
$a:1:2:3:4, $b: 5
3 голосов
/ 07 августа 2014

Я знаю, этому вопросу 4 года. Но я нашел ответ от ВЫ очень интересным, поскольку я не знал, что split может работать так. Поэтому я хочу дополнить его выдержкой из perldoc split , которая объясняет это поведение для новых читателей. : -)

my $str = "1:2:3:4:5";
my ($a, $b) = split /:([^:]+)$/, $str;
# Capturing everything after ':' that is not ':' and until the end of the string
# Now $a = '1:2:3:4' and $b = '5';

С Perldoc :

Если PATTERN содержит группы захвата, то для каждого разделителя создается дополнительное поле для каждой подстроки, захваченной группой (в порядке, в котором группы указаны, согласно обратным ссылкам); если какая-либо группа не совпадает, то вместо подстроки она захватывает значение undef. Кроме того, обратите внимание, что любое такое дополнительное поле создается всякий раз, когда есть разделитель (то есть всякий раз, когда происходит разделение), и такое дополнительное поле не учитывается по отношению к LIMIT. Рассмотрим следующие выражения, оцененные в контексте списка (каждый возвращенный список приведен в соответствующем комментарии):

split(/-|,/, "1-10,20", 3)
# ('1', '10', '20')

split(/(-|,)/, "1-10,20", 3)
# ('1', '-', '10', ',', '20')

split(/-|(,)/, "1-10,20", 3)
# ('1', undef, '10', ',', '20')

split(/(-)|,/, "1-10,20", 3)
# ('1', '-', '10', undef, '20')

split(/(-)|(,)/, "1-10,20", 3)
# ('1', '-', undef, '10', undef, ',', '20')
2 голосов
/ 18 марта 2010

Вы можете сделать это с помощью разделения и реверсирования следующим образом:

my $str="1:2:3:4:5";
my ($a,$b)=split(':',reverse($str),2); # reverse and split.

$a = reverse($a); # reverse each piece.
$b = reverse($b);

($a,$b) = ($b,$a); # swap a and b

Теперь $a будет 1:2:3:4 и $b будет 5.

Гораздо проще и понятнее использовать регулярные выражения, как это сделал Марк в своем ответе.

0 голосов
/ 17 июня 2016

Я немного опоздал на этот вопрос, но я собрал более общее решение:

# Similar to split() except pattern is applied backwards from the end of the string
# The only exception is that the pattern must be a precompiled regex (i.e. qr/pattern/)
# Example:
#   rsplit(qr/:/, 'John:Smith:123:ABC', 3) => ('John:Smith', '123', 'ABC')
sub rsplit {
    my $pattern = shift(@_);    # Precompiled regex pattern (i.e. qr/pattern/)
    my $expr    = shift(@_);    # String to split
    my $limit   = shift(@_);    # Number of chunks to split into

    # 1) Reverse the input string
    # 2) split() it
    # 3) Reverse split()'s result array element order
    # 4) Reverse each string within the result array
    map { scalar reverse($_) } reverse split(/$pattern/, scalar reverse($expr), $limit);
}

Он принимает аргументы, подобные split(), за исключением того, что разбиение выполняется в обратном порядке. Он также принимает предложение limit в случае, если вам нужно указанное количество элементов результата.

Примечание: эта подпрограмма ожидает предварительно скомпилированное регулярное выражение в качестве первого параметра.
split в Perl является встроенным и правильно интерпретирует /pat/, но попытка передать /pat/ в подпрограмму будет рассматриваться как sub($_ =~ /pat/).

Эта подпрограмма не пуленепробиваемая! Он работает достаточно хорошо для простых разделителей, но более сложные шаблоны могут вызвать проблемы. Сам шаблон не может быть полностью изменен, только выражение, с которым он сопоставляется.


Примеры:

rsplit(qr/:/, 'One:Two:Three', 2); # => ('One:Two', 'Three')

rsplit(qr/:+/, 'One:Two::Three:::Four', 3); # => ('One:Two', 'Three', 'Four')

# Discards leading blank elements just like split() discards trailing blanks
rsplit(qr/:/, ':::foo:bar:baz'); # => ('foo', 'bar', 'baz')
...