【问题标题】:Can you salvage my negative lookbehind example for commifying numbers?你能挽救我的负面回顾例子吗?
【发布时间】:2010-02-24 23:22:15
【问题描述】:

Mastering Perl 的“高级正则表达式”一章中,我有一个损坏的示例,我无法找到一个好的修复方法。这个例子可能是为了自己的利益而试图过于聪明,但也许有人可以为我解决它。里面可能有这本书的免费副本,用于修复工作。 :)

在讨论环视的部分中,我想使用否定的环视来实现带有小数部分的数字的 commifying 例程。关键是要使用负面的回顾,因为那是主题。

我愚蠢地这样做了:

$_ = '$1234.5678';
s/(?<!\.\d)(?<=\d)(?=(?:\d\d\d)+\b)/,/g;  # $1,234.5678

(?&lt;!\.\d) 断言(?=(?:\d\d\d)+\b) 之前的位不是小数点和数字。

愚蠢的是没有努力去打破它。通过在末尾添加另一个数字,现在有一组三位数字,前面没有小数点和一个数字:

$_ = '$1234.56789';
s/(?<!\.\d)(?<=\d)(?=(?:\d\d\d)+\b)/,/g;  # $1,234.56,789

如果在 Perl 中lookbehinds 可以是可变宽度,这将非常容易。但他们不能。

请注意,在没有消极的后视情况下很容易做到这一点,但这不是示例的重点。有没有办法挽救这个例子?

【问题讨论】:

  • 仅供参考,您在发布此内容时遗漏了\b(?:\d\d\d)+\b),所以我添加了它。 (我查了一下,它在书中。)不过,这只是分散注意力;它与后视问题无关。
  • 啊,是的,谢谢。当我从我的电子邮件中复制并粘贴它来测试它时,某些东西将 \b 转换为 ^B 并将所有内容都搞砸了。我忘记重新添加了。
  • 你可能想考虑在这个问题上给予额外的动力(尽管这本书非常周到!),因为你有多余的代表。 :) 它还可以让您获得更多关注,因为可以从首页搜索具有活跃赏金的问题。
  • @FM:问题的重点是使用(?&lt;!)。我不是在寻找绕过它的方法。
  • 很惊讶没有人编辑标题...对措辞大笑

标签: regex perl lookbehind


【解决方案1】:

我认为没有某种形式的可变宽度后视是不可能的。在 5.10 中添加的\K 断言提供了一种伪造可变宽度正向后视的方法。我们真正需要的是可变宽度的 negative look-behind,但只要有一点创造力和很多丑陋,我们就可以让它工作:

use 5.010;
$_ = '$1234567890.123456789';
s/(?<!\.)(?:\b|\G)\d+?\K(?=(?:\d\d\d)+\b)/,/g;
say;  # $1,234,567,890.123456789

如果曾经有一种模式需要/x 表示法,那就是这个:

s/
  (?<!\.)        # Negative look-behind assertion; we don't want to match
                 # digits that come after the decimal point.

  (?:            # Begin a non-capturing group; the contents anchor the \d
                 # which follows so that the assertion above is applied at
                 # the correct position.

    \b           # Either a word boundary (the beginning of the number)...

    |            # or (because \b won't match at subsequent positions where
                 # a comma should go)...

    \G           # the position where the previous match left off.

  )              # End anchor grouping

  \d+?           # One or more digits, non-greedily so the match proceeds
                 # from left to right. A greedy match would proceed from
                 # right to left, the \G above wouldn't work, and only the
                 # rightmost comma would get placed.

  \K             # Keep the preceding stuff; used to fake variable-width
                 # look-behind

                 # <- This is what we match! (i.e. a position, no text)

  (?=            # Begin a positive look-ahead assertion

    (?:\d\d\d)+  # A multiple of three digits (3, 6, 9, etc.)

    \b           # A word (digit) boundary to anchor the triples at the
                 # end of the number.

  )              # End positive look-ahead assertion.
/,/xg;

【讨论】:

  • 太棒了。自从我在 5.10 之前编写了 Mastering Perl 以来,我从未考虑过使用 \K。我想我可以完成这项工作,如果只是为了一个非常聪明的例子,我可以指出可变宽度后视的极端困难。 \G 也很重要。那是一本给你的免费书。如果您已经有Mastering Perl,请告诉我我还能为您提供哪本书。 :)
  • 虽然我很高兴在问题的约束范围内找到解决方案,但我对我的创作感到有些震惊,尤其是在零宽度断言之间使用交替。我需要use re 'debug' 来确定@​​987654327@ 是必要的。我只是为了好玩才对它进行基准测试,它比常见问题解答快 10% 左右。那可能是因为它不使用捕获。我没有Mastering Perl 的副本,那太好了。嗯...这里没有 PM 系统,但您应该可以通过我的 CPAN 作者 ID (MJCARMAN) 联系到我。
  • 为了它的价值,我在 Mastering Perl, 2nd Edition 中重新使用了这个例子,但不是为了炫耀前瞻。我曾经说明 \K :)
  • @briandfoy:酷!我很高兴能提供帮助。
【解决方案2】:

如果必须在 Stack Overflow 上发帖询问是否有人能想出如何通过消极的后视来做到这一点,那么这显然不是消极后视的一个很好的例子。你最好想出一个新的例子,而不是试图挽救这个。

本着这种精神,自动拼写纠正器怎么样?

s/(?<![Cc])ei/ie/g; # Put I before E except after C

(显然,这不是英语中的硬性规定,但我认为它是否定后向的更现实的应用。)

【讨论】:

  • 是的,我想我不得不放弃这个例子,这太糟糕了。不过,我已经有像你描述的那样简单的例子。但是,我也应该说,仅仅因为我无法弄清楚并不意味着它不是一个好例子。学习任何东西的最好方法是写一本关于它的书。我从我的技术审阅者那里学到了很多东西。 :)
  • 我刚刚注意到斜体字“you”。我认为在 Stackoverflow 上有很多人比我更聪明、更擅长 Perl。我经常来这里。 :)
  • @cjm 您能否添加您所指的拼写规则的示例?我连英语都不懂拼写规则。 .-) 另外我认为您的意思是“交换 i 和 e”而不是“将 i 放在 e 之前”。
  • @AloisMahdal,有plenty of examples on Wikipedia
【解决方案3】:

我不认为这就是你所追求的(特别是因为消极的后视断言已被删除),但我想,你唯一的选择是像这个例子中那样吞下小数位:

s/
  (?:
    (?<=\d)
    (?=(?:\d\d\d)+\b)
   |
    ( \d{0,3} \. \d+ )
  )
 / $1 ? $1 : ',' /exg;

附:我认为这是一个很好的例子,当它不用作本书的第一个例子时,因为它展示了环视断言的一些陷阱和局限性。

【讨论】:

  • 它实际上是本书中关于这些东西的最后一个例子。然而,这个答案的问题在于 (?&lt;!\.) 没有做任何事情。如果你删除它,你会得到相同的答案。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 2016-05-19
  • 1970-01-01
  • 2016-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多