【问题标题】:RegEx Challenge: Capture all the numbers in a specific row正则表达式挑战:捕获特定行中的所有数字
【发布时间】:2016-06-20 10:06:31
【问题描述】:

假设我们有这个文本:

...
settingsA=9, 4.2 
settingsB=3, 1.5, 9, 2, 4, 6
settingsC=8, 3, 2.5, 1
...

问题是如何一步一步捕获特定行中的所有数字?

单步意味着:

  • 单个正则表达式模式。
  • 单一操作(无循环或拆分等)
  • 所有匹配都被捕获在一个数组中。

假设我想捕获以settingsB= 开头的行中的所有数字。最终结果应如下所示:

3
1.5
9
2
4
6

我的失败尝试:

<?php
    $subject =
        "settingsA=9, 4.2
         settingsB=3, 1.5, 9, 2, 4, 6
         settingsC=8, 3, 2.5, 1";

    $pattern = '([\d\.]+)(, )?' // FAILED!
    $pattern = '(?:settingsB=)(?:([\d\.]+)(?:, )?)' // FAILED!
    $pattern = '(?:settingsB=)(?:([\d\.]+)(?:, )?)+' // FAILED!
    $pattern = '(?<=^settingsB=|, )([\d+\.]+)' // FAILED!

    preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
    if ($matches) {
        print_r($matches);
    }
?>

更新 1: 不幸的是,@Saleem 的示例使用了多个步骤而不是单个步骤。我并不是说他的例子不好(它实际上有效),但我想知道是否有另一种方法可以做到这一点以及如何做到这一点。有什么想法吗?

更新 2: @bobble bubble 为这一挑战提供了完美的解决方案。

【问题讨论】:

  • 请提及您的环境、语言。例如PHP、python、java、JavaScript等甚至编辑器、Notepad++
  • 我猜是通用引擎,没有语言,除了正则表达式。
  • 是的,但这将是一个多阶段的解决方案。捕获+分词器
  • 我已经在 python 中发布了解决方案,因为 php 目前不可用。但是 python 和 php 正则表达式非常相似。

标签: php regex text notepad++ pcre


【解决方案1】:

您可以使用\G anchor 将匹配项粘合到上一个匹配项的末尾。这种在所需部分之前也使用\K to reset 的模式将适用于 PCRE 正则表达式风格。

(?:settingsB *=|\G(?!^) *,) *\K[\d.]+
  • (?: 会打开一个non-capturing group 以供交替使用
  • 匹配settingsB,后跟 *任意数量的空间,后跟文字=
  • |\G(?!^) 或从上一场比赛结束但未开始的地方继续
  • *, 并匹配前面有可选空格的逗号
  • ) 交替结束(非捕获组)
  • *\K 可选空格后重置
  • [\d.]+ 匹配一个或多个数字和句点。

如果序列包含制表符或换行符,请使用\s 作为空白字符 而不是空格。

See demo at regex101PHP demo at eval.in

or this more compatible pattern 使用 capturing group 而不是 \K,这应该适用于支持 \G 锚(Java、.NET、Ruby...)的任何正则表达式风格

【讨论】:

  • 简直完美!这就是我所说的正则表达式魔法。感谢您的解决方案。
【解决方案2】:

这里是 python 解决方案,但稍后会发布 PHP rx。但是,python regex 和 php 很相似。

(?<=settingsB=)(\d+(?:\.\d+)?(?:, )?)+

Python:

import re

subject = """
...
settingsA=9, 4.2
settingsB=3, 1.5, 9, 2, 4, 6
settingsC=8, 3, 2.5, 1
...
"""

rx = re.compile(r"(?<=settingsB=)(\d+(?:\.\d+)?(?:, )?)+", re.IGNORECASE)
result = rx.search(subject)

if result:
    numString = result.group()

    for n in [f.strip() for f in numString.split(',')]:
        print(n)

PHP

$subject =
    "settingsA=9, 4.2
     settingsB=3, 1.5, 9, 2, 4, 6
     settingsC=8, 3, 2.5, 1";

$pattern = '/(?<=settingsB=)(\d+(?:\.\d+)?(?:, )?)+/i';
preg_match($pattern, $subject, $matches);

if ($matches) {

    $num = explode(",", $matches[0]);

    for ($i = 0; $i < count($num); $i++) {
        print(trim($num[$i]) . "\n");
    }
}

输出:

3
1.5
9
2
4
6

【讨论】:

  • 我将它粘贴到 regex101.com 中,它只匹配最后一个数字 - 即使正则表达式设置为 Python。知道为什么吗?
  • @Saleem,感谢您的回复,但必须有更好的解决方案。我想在数组中返回结果而不拆分,例如。 (numString.split)。让正则表达式进行拆分。有什么想法吗?
  • @JerryJeremiah,这是因为 regex101 仅显示最后一组。请看regex101.com/r/wD4vQ5/1
  • @OlavH,是的,我同意。应该有更好的解决方案,但在短时间内有总比没有好:)
  • 只是为了好玩:你知道premature optimization is the root of all evil 一种解决方案是应用两个不同的正则表达式。一个像我最初那样捕获整个字符串,然后在上次捕获的结果上应用另一个正则表达式以消除昏迷 ` , `
猜你喜欢
  • 1970-01-01
  • 2013-11-19
  • 1970-01-01
  • 2019-10-08
  • 1970-01-01
  • 1970-01-01
  • 2018-10-02
  • 2013-06-27
  • 1970-01-01
相关资源
最近更新 更多