3

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

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

" aa{bb{dccd"

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

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

5

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

我发现了类似Perl Guru 论坛的帖子:Perl Programming Help: I​​ntermediate: split or splice string on char count? ,建议substr在类似的上下文中使用;但是,我必须根据substr上面的示例编写两个 s 来填充列表,因此我宁愿听到有关 substr 的替代方案。

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

编辑:

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

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

4

6 回答 6

3
my ($pre, $post) = split /\{(?!.*\{)/s, $s;

或者

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

第二个可能更好。

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

于 2012-06-08T19:42:49.737 回答
3

你写了:

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

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

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

有时您更愿意将数据视为在特定列中被分割。例如,您可能希望在位置 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);

我相信这就是你所要求的。

于 2012-06-08T19:53:51.657 回答
2

这里有一些方法:

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

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

于 2012-06-08T19:42:42.580 回答
1

使用方法rindexsubstr根据{.

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

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";

与前面的代码具有相同的输出。

于 2012-06-08T19:58:39.820 回答
1

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

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

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

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

于 2012-06-08T21:19:04.033 回答
0

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

感谢这些资源:

...我了解了“大括号”正则表达式运算符,{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),但不知道如何:)

干杯!

于 2012-06-08T19:50:22.640 回答