使用正则表达式匹配重复模式

Gyp*_*aut 2 grep regular-expression pcre

假设我有一个如下文件

1,2,3-5,6
1,2,3-5,6,
1
1-3
1,2,3-,4,5-7
1,2,3-,4,5-7,
1,2,-3,4,5
1,2,-,3,4
1,2,,,3,4
,1,2,3
Run Code Online (Sandbox Code Playgroud)

只有以下规则的组合才被视为有效:

  1. 范围 [0-9]+-[0-9]+
  2. 团体 [0-9]+,[0-9]+
  3. 单号 [0-9]+

以逗号结尾的行也应该被认为是有效的

我只想提取

1,2,3-5,6
1,2,3-5,6,
1
1-3
Run Code Online (Sandbox Code Playgroud)

由于下面显示的其他行不符合规则

1,2,3-,4,5-7
1,2,3-,4,5-7,
1,2,-3,4,5
1,2,-,3,4
1,2,,,3,4
,1,2,3
Run Code Online (Sandbox Code Playgroud)

因为有些行的范围不完整,有些行缺少组中的数字


PS:唯一PCRE兼容的grep解决方案会很棒,但也欢迎其他解决方案

ImH*_*ere 7

与您列出的字符串(以及以 a 开头的字符串)匹配的完整 pcre,可能是:

grep -P '^((^|,)([0-9]+(-[0-9]+)?(,|$))+)$'
Run Code Online (Sandbox Code Playgroud)

我们是怎么到那里的?

要匹配的最基本元素是数字,假设[0-9]\dPCRE 中更简单的元素是英文 (ASCII) 数字的正确正则表达式。这也可能不是。例如,它可以匹配梵文数字。那么你需要写:[0123456789]准确地说。

然后,一串数字将被匹配[0-9]+

在一个数字(1 或 3 或 26)之后可能有一个破折号“-”,后跟一个或几个数字(又是一个数字):

[0-9]+(-[0-9]+)?
Run Code Online (Sandbox Code Playgroud)

其中?使破折号序列可选。

然后,这些数字中的每一个:(3或数字范围:)4-9后面应该跟一个逗号,(多次):

([0-9]+(-[0-9]+)?,)+
Run Code Online (Sandbox Code Playgroud)

除了最后一个逗号可能会丢失:

([0-9]+(-[0-9]+)?(,|$))+
Run Code Online (Sandbox Code Playgroud)

并且,如果需要,可能会出现一个前导逗号:

(^|,)([0-9]+(-[0-9]+)?(,|$))+
Run Code Online (Sandbox Code Playgroud)

将正则表达式锚定到测试文本的开头和结尾是一个很好的主意:

^((^|,)([0-9]+(-[0-9]+)?(,|$))+)$
Run Code Online (Sandbox Code Playgroud)

您可以在本站测试和编辑PCRE 正则表达式

如果应拒绝前导逗号,请使用:

^(([0-9]+(-[0-9]+)?(,|$))+)$
Run Code Online (Sandbox Code Playgroud)

这对正则表达式机器没有任何可选的解释。所有都必须匹配,任何不匹配的都会被拒绝。

它可以写成一个(GNU)扩展正则表达式:

grep -E '^(([0-9]+(-[0-9]+)?(,|$))+)$'
Run Code Online (Sandbox Code Playgroud)

作为基本正则表达式 (BRE):

grep '^\(\([0-9]\{1,\}\(-[0-9]\{1,\}\)\{0,1\},\{0,1\}\)\{1,\}\)$'
Run Code Online (Sandbox Code Playgroud)

如果逗号,是可选的{0,1},正则表达式引擎可能会决定匹配什么。


Kus*_*nda 5

使用awk分解每一行到逗号分隔的字段,然后在拆分成破折号子字段的字段内容,同时删除包含不必要的字段或子字段行:

BEGIN { FS = "," }

{
    for (i = 1; i <= NF; ++i) {
        # Only the 1st field is allowed to be
        # empty, but only if there are further
        # fields (avoids empty lines).

        if ($i == "" && (i != 1 || NF == 1)) next

        # If the field is split on dashes, it
        # should split into no more than two
        # elements.

        if ((n = split($i, a, "-")) > 2) next

        # Each split-up element needs to be made
        # up of decimal digits only.

        for (j = 1; j <= n; ++j)
            if (a[j] !~ "^[[:digit:]]+$") next
    }

    # The current line is ok to print.

    print
}
Run Code Online (Sandbox Code Playgroud)

这将被用作

awk -f script file
Run Code Online (Sandbox Code Playgroud)

其中,script持有的awk程序。

或者,作为“一次性”:

awk -F, '{for(i=1;i<=NF;++i){if(($i==""&&(i!=1||NF==1))||((n=split($i,a,"-"))>2))next;for(j=1;j<=n;++j)if(a[j]!~"^[[:digit:]]+$")next}};1' file
Run Code Online (Sandbox Code Playgroud)

您可以5-2j循环后轻松添加对“向后范围”(例如)的检查:

if (n == 2 && a[1] > a[2]) next
Run Code Online (Sandbox Code Playgroud)

  • @GypsyCosmonaut 我真的不喜欢在其他人的解决方案没有任何问题的情况下寻找负面的说法。大型正则表达式的主要问题是它们是“只写”的,即很难阅读和理解它们,因此很难修改和扩展它们,也很难纠正它们中的错误。这取决于你为谁编写代码。我经常编写需要同事和互联网上的随机人员理解的代码,并且我倾向于更喜欢可读性而不是紧凑的代码块。 (2认同)