我找到index
和rindex
来查找字符串中第一个或最后一个字符(或子字符串)。我也知道他们有offset
可以用来从某个索引开始。
我想知道的是,是否有一种简单的方法可以找到字符串中第N个字符或子字符串的索引。我不想用正则表达式来做这件事,而宁愿不要写一个只重复调用带有偏移的index
的循环。
答案 0 :(得分:1)
一种可能的实施方式:
use strict;
use warnings;
use feature qw(say);
my $string = 'the quick brown fox jumped over the lazy dog';
my $substring = 'o';
my $n = 4;
sub nth_index {
my ($string, $substring, $n) = @_;
my ($times, $index) = (0, 0);
while ( $times < $n && $index != -1 ) {
$index = index(
$string,
$substring,
$times == 0
? 0
: $index + length($substring),
);
$times++;
}
return $index;
}
say nth_index($string, $substring, $n); # 42
答案 1 :(得分:1)
如上所述,没有内置功能。以下是一些使用split
,index
和正则表达式的方法。
use warnings;
use strict;
use feature qw(say);
my $str = "Xab_ab_ab_ab_"; # 'Xab_ab'; # test failed (3) matches
my $N = 3;
foreach my $patt qw(a ab c) {
say "Find index of occurrence $N of |$patt| in: |$str|";
say "index: ", ( ind_Nth_match_1($str, $patt, $N) // "no $N matches" ); #/
say "split: ", ( ind_Nth_match_2($str, $patt, $N) // "no $N matches" ); #/
say "regex: ", ( ind_Nth_match_3($str, $patt, $N) // "no $N matches" ); #/
}
sub ind_Nth_match_1 {
my ($str, $patt, $N) = @_;
my ($pos, $cnt) = (0, 0);
while ($pos = index($str, $patt, $pos) + 1) { # != 0
return $pos-1 if ++$cnt == $N;
}
return;
}
sub ind_Nth_match_2 {
my ($str, $patt, $N) = @_;
my @toks = split /($patt)/, $str;
return if @toks < 2*$N;
return length( join '', @toks[0..2*$N-1] ) - length($patt);
}
sub ind_Nth_match_3 {
my ($str, $patt, $N) = @_;
my $cnt = 0;
while ($str =~ m/$patt/g) {
return $-[0] if ++$cnt == $N;
}
}
打印
Find index of occurrence 3 of |a| in: |Xab_ab_ab_ab_| index: 7 split: 7 regex: 7 Find index of occurrence 3 of |ab| in: |Xab_ab_ab_ab_| index: 7 split: 7 regex: 7 Find index of occurrence 3 of |c| in: |Xab_ab_ab_ab_| index: no 3 matches split: no 3 matches regex: no 3 matches
注释
在split
中,每个分隔符也会在输出列表中返回,并带有捕获/($patt)/
,以便进行更简单的length
估算。因此,我们会计算2*$N
(然后选择-1
)。
在正则表达式中,使用@- array
@LAST_MATCH_START
作为上次成功匹配的位置。这里/g
中标量上下文中的while
使其在重复执行中从匹配跳转到下一个匹配,$-[0]
给出了上一个(上一个)匹配的起始位置。
如果没有必要undef
匹配,则返回$N
,包括根本不匹配。
感谢Borodin对来自潜艇的return
以及使用@-
代替@+
的评论。
答案 2 :(得分:1)
以下是我将如何解决问题的两个例子
子例程nth_index1
使用index
,而nth_index2
使用正则表达式。两者都涉及循环,因为任何解决方案都必须
我相信正则表达式解决方案更具可读性,而index
解决方案可能更快一点。但它们都是如此之快,以至于它们极不可能造成瓶颈,可读性始终是最重要的
use strict;
use warnings 'all';
my $s = 'the quick brown fox jumps over the lazy dog';
my $ss = 'o';
for my $n ( 1 .. 4 ) {
printf "%d %d\n",
nth_index1($s, $ss, $n),
nth_index2($s, $ss, $n);
}
sub nth_index1 {
my ($s, $ss, $n) = @_;
my $i;
my $len = length $ss;
while ( $n-- ) {
$i = index($s, $ss, $i ? $i + $len : 0 );
return if $i < 0;
}
$i;
}
sub nth_index2 {
my ($s, $ss, $n) = @_;
while ( $s =~ /$ss/g ) {
return $-[0] unless --$n;
}
return;
}
12 12
17 17
26 26
41 41
答案 3 :(得分:0)
(这个答案没有回答你的问题,但是可以帮助你接受正则表达式解决方案。)
你要求提供位置的数字索引的方式,听起来你正在考虑一旦你有这个数字就从字符串中提取数据,就像C程序员可能会这样做。
例如,假设你有字符串
my $str = "My daddy left home when I was three and he didn't leave much for ma and me";
并且您希望将所有数据提取到单词“and”的第一个实例。这是你可以做到的方式,这是一种使用Perl来实现它的方法。
my $pos = find_index_of_first_occurrence( $str, 'and' );
# Note that find_index_of_first_occurrence() is a hypothetical function.
print substr( $str, 0, $pos );
# Prints "My daddy left home when I was three "
使用正则表达式在Perl中执行此操作的方式要简单得多。
$str =~ /^(.*?)and/;
print $1;
使用正则表达式,您可以在一次操作中组合搜索字符串和提取数据。 (请注意,为了简单起见,两个代码片段都忽略了根本没有找到“和”的情况)
我知道你还没有很好地了解正则表达式,并且一开始这些正则表达式令人生畏,但是如果你想要成功使用该语言,你需要将它们理解为学习Perl的一部分。 / p>