查找字符串中第N个字符出现的索引

时间:2016-07-18 17:48:48

标签: string perl

我找到indexrindex来查找字符串中第一个或最后一个字符(或子字符串)。我也知道他们有offset可以用来从某个索引开始。

我想知道的是,是否有一种简单的方法可以找到字符串中第N个字符或子字符串的索引。我不想用正则表达式来做这件事,而宁愿不要写一个只重复调用带有偏移的index的循环。

编辑:我没有很好地说明限制。我说的原因"没有循环"我正在寻找一种内置的方式,它以多种语言存在。

4 个答案:

答案 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)

如上所述,没有内置功能。以下是一些使用splitindex和正则表达式的方法。

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>