【问题标题】:Split string (or regex match) at position/index of nth character in Perl?在Perl中第n个字符的位置/索引处拆分字符串(或正则表达式匹配)?
【发布时间】:2012-06-12 21:10:54
【问题描述】:

有一个类似措辞的问题,但我认为这略有不同。

基本上,假设我有这个字符串:

aa{bb{dccd

这里我想在最后一个大括号{处分割字符串;并将零件作为数组返回。我可以使用rindex 轻松找到该字符的位置(从 0 开始的索引):

perl -e '
$aa="aa{bb{dccd" ;
$ri = rindex($aa, "{") ;
print "$ri\n"; '

5

...鉴于我不是 Perl 编码器,我首先想到的是使用类似 $str = split($aa, 3) 之类的东西。不幸的是,这不是正确的语法 - split 将正则表达式作为第一个参数(匹配的内容),将字符串作为第二个参数 - 它不采用整数位置索引作为参数。

我发现类似Perl Guru Forums: Perl Programming Help: Intermediate: split or splice string on char count? 的帖子,推荐在类似的上下文中使用substr;但是,我必须根据上面的示例编写两个substrs 来填充列表,所以我宁愿听到关于 substr 的替代方案。

基本上,如果匹配第 N 个字符的位置的问题可以表示为正则表达式匹配,那么 split 也可以工作 - 所以这将是我的主要问题。但是,我也很想知道是否有 Perl 内置函数可以接受指定字符位置的整数列表/数组,并返回包含拆分部分的数组。

编辑:

总结以上内容-我想要字符索引,因为我想将它们打印出来以进行调试;同时,使用它们将字符串拆分为数组 - 但不使用substrs。

EDIT2:我刚刚意识到我在 OP 中遗漏了一些东西——也就是说,在我正在处理的问题中,我必须首先检索字符索引(通过 rindex 或其他方式);然后我必须对它们进行计算(因此它们可能会增加或减少) - 只有这样我才应该拆分字符串(基于新的索引值)。可能是我原来的例子太简单了,并没有过多地表达对索引/字符位置的关注(更不用说我第一次想到split 暗示了字符索引——但我真的不能记住它来自哪个编程语言:))

【问题讨论】:

  • 如果您的问题需要的不是我的答案,我不知道它是什么。
  • 感谢@tchrist 的评论;事实是,它是正确的 - unpack 可用于拆分为单行数组,是内置的,并且可以接受字符索引(我要求的所有内容) - 但是,split 也可以本身,带有相应的正则表达式。所以现在我不太确定要接受什么(因为这对我来说是相当多的新信息和有些出乎意料的信息,所以我要等一会儿,直到我接受答案,因为我真的很感激以下所有示例)。干杯!
  • 我只是想确定对您问题的编辑是否改变了问题的性质,从而需要不同的答案。
  • 嗨@tchrist - 我不这么认为;我只是试图澄清为什么标题中提到的字符位置很重要(现在我可以看到该示例的简单性使其具有误导性,因为确实 - 不一定需要索引来解析该示例Perl)。一旦我解决了我的想法,我可能应该再次重新编辑这个问题:)干杯!

标签: regex string perl split


【解决方案1】:

你写道:

我也很想知道是否有 Perl 内置函数可以接受指定字符位置的整数列表/数组,并返回包含拆分部分的数组。

要创建一个函数,该函数采用偏移列表并生成具有这些拆分位置的子字符串列表,请将偏移转换为长度并将它们作为参数传递给unpack

Perl Cookbook 的第 1 章中有一个 &cut2fmt 函数可以做到这一点。以下是摘录,经作者许可转载:

有时您更愿意将您的数据视为在特定列中被分割。例如,您可能想要放置 在位置 8、14、20、26 和 30 之前剪切。这些是每个字段开始的列号。尽管您可以计算出正确的unpack 格式是"A7 A6 A6 A6 A4 A*",但这对于非常懒惰的Perl 程序员来说是太大的精神压力。让 Perl 为您解决。使用下面的cut2fmt 函数:

sub cut2fmt {
      my(@positions) = @_;
      my $template   = '';
      my $lastpos    = 1;
      foreach $place (@positions) {
          $template .= "A" . ($place - $lastpos) . " ";
          $lastpos   = $place;
      }
      $template .= "A*";
      return $template;
  }

  $fmt = cut2fmt(8, 14, 20, 26, 30);
  print "$fmt\n";

  A7 A6 A6 A6 A4 A*

所以你的使用方式是这样的:

$fmt = cut2fmt(8, 14, 20, 26, 30);
@list = unpack($fmt, $string);

或直接作为

@list = unpack(cut2fmt(8, 14, 20, 26, 30), $string);

我相信这就是你想要的。

【讨论】:

  • 非常感谢@tchrist 的挖掘,非常有帮助(而且“精神压力太大”——确实是:))干杯!
  • 嗨@tchrist - 现在头脑更清晰了,我接受了这个答案,因为它显示了通过unpack按索引拆分的最通用解决方案;我只想补充一点,像/.{$ind}(.)/ 这样的带有大括号运算符的正则表达式也可以用作unpack 的替代品。再次非常感谢 - 干杯!
【解决方案2】:
my ($pre, $post) = split /\{(?!.*\{)/s, $s;

my ($pre, $post) = $s =~ /^(.*)\{(.*)/s;

第二个可能更好。

如果您需要{ 的索引,请使用length($pre)。 (使用第二种解决方案,您也可以使用$-[2] - 1。请参阅perlvar 中的@-@+。)

【讨论】:

  • 谢谢你,@ikegami - 但不幸的是,这不涉及索引;不过,我在this post 中找到了一部分。干杯!
  • @sdauu, length($pre) 为您提供索引。更新节点以满足这一需求。
【解决方案3】:

这里有一些方法:

split /.*\K{/, $str;
split /{(?!.*{)/, $str;
$str =~ /(.*){(.*)/;

如果字符串可以跨多行,请使用/regex/s

【讨论】:

  • 谢谢你,@Qtax - 但不幸的是,这不涉及字符索引;不过,我在this post 中找到了一部分。干杯!
  • @sdaau,你是什么意思?这为您的问题提供了不同的工作答案“我想在最后一个大括号{ 处拆分字符串;并将部分作为数组返回”,ikegami 的回答也是如此。跨度>
  • 谢谢你,@Qtax - 事实上,对于这个问题,它是一个答案;但是,我一直在寻找一种方法,既可以将字符位置保留为分隔符,又可以使用它们将字符串拆分为数组(但不使用 substr),并尽可能接近拆分的行为方式(即@array = split (...)) .在我的帖子中,我设法将索引直接拆分——这样就接近了我想要的。我试图澄清OP,希望现在更好。干杯!
【解决方案4】:

使用rindex的方法是使用substr根据{的位置提取字符串的两部分。

请注意,这包括后缀部分中的{。要排除它,您将在第二个 substr 调用中使用 $i + 1

my $str = "aa{bb{dccd";

my $i = rindex $str, '{';
my $pref = substr $str, 0, $i;
my $suff = substr $str, $i;

print $pref, "\n";
print $suff, "\n";

输出

aa{bb
{dccd

更新

我刚刚读到您希望避免使用substr 并在一次操作中进行拆分。 unpack 会为你做到这一点,就像这样

my $str = "aa{bb{dccd";

my $i = rindex $str, '{';

my ($pref, $suff) = unpack "A$i A*", $str;

print $pref, "\n";
print $suff, "\n";

输出与之前的代码相同。

【讨论】:

  • 谢谢你,@Borodin - 但我实际上是在寻找远离substr 的方法,更接近split 的语法(我确实设法找到了this post,只需要去掉空白:))干杯!
  • 非常感谢编辑,@Borodin - unpack 看起来很不错;接受索引,一次调用并返回一个数组......干杯!
【解决方案5】:

我仍然不明白这有什么困难。您是否不想丢弃大括号(或您的分隔符)? @Qtax 解决方案的这些改编将大括号留在第一个或第二个子字符串中:

# split before the brace
split /.*\K(?=\{)/, $str;
split /(?=\{(?!.*\{))/, $str;
$str =~ /(.*)(\{.*)/;

# split after the brace
split /.*\{\K)/, $str;
split /(?<=\{(?!.*\{))/, $str;
$str =~ /(.*\{)(.*)/;

(我知道没有必要避开大括号,但我认为这样阅读会更容易一些。)

【讨论】:

  • 谢谢你,@AlanMoore - 我试图在 OP 中更好地澄清;我基本上首先采用字符索引(我使用了rindex,但现在意识到我也可以使用正则表达式,并获取其匹配的索引) - 然后我对它们进行计算 - 最后我需要拆分修改后的值。您帖子中的表达式,虽然它们确实在我提供的简化示例中正确执行了解析,但不要“接受”索引/位置值 - 这是我最初的问题(并且很抱歉我没有在我的 OP 中澄清得很好)。干杯!
【解决方案6】:

好的,我会发布这个作为答案,这就是我得到的结果。

感谢这些资源:

...我了解了“大括号”正则表达式运算符{n},它“匹配前面的字符或字符范围,n 次精确”。因此,我可以匹配/.{5}(.)/

perl -e '
$aa="aa{bb{dccd" ;
$aa =~ /.{5}(.)/  && print "--${1}--\n"; '

--{--

这会选择前 5 个“任意”字符 - 然后选择并打印下一个字符。或者:

/               # start regex
 {              # match "{" character
  {5}           # repeat previous five times
     (.)        # select into match group (the $1) next character
        /       # end regex

所以,最后,我可以使用rindex 来执行这样的拆分:

perl -e '
$aa="aa{bb{dccd" ;
$ri = rindex($aa, "{") ;
$aa =~ /.{$ri}(.)/  && print "--${1}--\n";
@res = split(/^.{$ri}(.)/, $aa);
print join("; ", @res) . "\n"; '

--{--
; {; dccd

.. 但考虑到这也需要在开始时进行一些捕获,所以这里有其他变体:

@res = split(/^(.{$ri})(.)/, $aa);

--{--
; aa{bb; {; dccd


@res = split(/^(.{$ri})./, $aa);

--{--
; aa{bb; dccd

...这两个都对我有用 - 除了我有一个空白作为第一项,我想一次性摆脱它(不调用额外的splice),但不知道如何:)

干杯!

【讨论】:

  • 您的split 正在成为一种简单地应用正则表达式的尴尬方式。你也可以写@res = $aa =~ /^(.{$i})(.)(.*)/,或者更好的是,让正则表达式引擎为你找到最后一个大括号@res = $str =~ /^(.*)(\{)(.*)/
  • @Borodin - 确实很尴尬;我想我从某个地方记得一个“拆分”函数,它接受索引而不是正则表达式匹配,所以我试图让这个函数尽可能接近这种行为:) 事实是,我不知道我可以获得对正则表达式匹配索引的引用,但在 ikegami 的帖子中评论刚刚提到它。干杯!
  • 如果您只是让正则表达式为您进行拆分,我不确定您是否知道您不需要 正则表达式匹配的索引?您是否需要索引用于其他目的?
  • 为什么要这么复杂。 split /(.*){/ 还不够好用吗?
  • @sdaau - 没问题。阅读有关使用 regex 进行 Perl 拆分的信息。正则表达式在 split 中匹配的内容不会添加到数组中,除非它在捕获括号中。如果是,它是一个新的注入元素。另一部分是.*,正如其他人所说,将匹配所有内容直到最后一个}。如果是.*?,它将匹配第一个/下一个}。
猜你喜欢
  • 2012-09-17
  • 2022-01-17
  • 1970-01-01
  • 2011-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-04
相关资源
最近更新 更多