你能挽救我的负面回顾示例来传达数字吗?

发布于 2024-08-22 20:30:12 字数 756 浏览 5 评论 0原文

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

在讨论环视的部分中,我想使用负环视来实现带有小数部分的数字的通信例程。重点是使用负面回顾,因为这就是主题。

我愚蠢地这样做了:

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

(? 断言 (?=(?:\d\d\d)+\b)< 之前的位/code> 不是小数点和数字。

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

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

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

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

In the "Advanced Regular Expresssion" chapter in Mastering Perl, I have a broken example for which I can't figure out a nice fix. The example is perhaps trying to be too clever for its own good, but maybe someone can fix it for me. There could be a free copy of the book in it for working fixes. :)

In the section talking about lookarounds, I wanted to use a negative lookbehind to implement a commifying routine for numbers with fractional portions. The point was to use a negative lookbehind because that was the topic.

I stupidly did this:

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

The (?<!\.\d) asserts that the bit before the (?=(?:\d\d\d)+\b) is not a decimal point and a digit.

The stupid thing is not trying hard enough to break it. By adding another digit to the end, there is now a group of three digits not preceded by a decimal point and a digit:

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

If lookbehinds could be variable width in Perl, this would have been really easy. But they can't.

Note that it's easy to do this without a negative lookbehind, but that's not the point of the example. Is there a way to salvage this example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

恰似旧人归 2024-08-29 20:30:12

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

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;

I don't think it's possible without some form of variable-width look-behind. The addition of the \K assertion in 5.10 provides a way of faking variable-width positive look-behind. What we really need is variable-width negative look-behind but with a little creativity and a lot of ugliness we can make it work:

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

If there was ever a pattern that begged for the /x notation it's this one:

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;
爱她像谁 2024-08-29 20:30:12

如果必须在 Stack Overflow 上发帖询问是否有人可以弄清楚如何使用负向后查找来做到这一点,那么这显然不是消极向后查找的好例子。你最好想出一个新的例子,而不是试图挽救这个例子。

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

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

(显然,这不是英语中的硬性规则,但我认为这是负向回顾的更现实的应用。)

If you have to post on Stack Overflow asking if somebody can figure out how to do this with negative lookbehind, then it's obviously not a good example of negative lookbehind. You'd be better off thinking up a new example rather than trying to salvage this one.

In that spirit, how about an automatic spelling corrector?

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

(Obviously, that's not a hard and fast rule in English, but I think it's a more realistic application of negative lookbehind.)

倦话 2024-08-29 20:30:12

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

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

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

I don't think this is what you are after (especially becaue the negative look-behind assertion has been dropped), but I guess, your only option is to slurp up the decimal places like in this example:

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

P.S. I think it is a good example when not used as the first one in the book, as it demonstrates some of the pitfalls and limitations of look-around assertions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文