【问题标题】:How can I specify an optional capture group in this RegEx?如何在此 RegEx 中指定可选的捕获组?
【发布时间】:2010-10-04 01:38:59
【问题描述】:

如何修复此 RegEx 以选择性地捕获文件扩展名?

我正在尝试将字符串与可选组件匹配,但似乎有问题。 (匹配的字符串来自打印机日志。)


我的正则表达式(.NET Flavor)如下:

.*(header_\d{10,11}_).*(_.*_\d{8}).*(\.\w{3,4}).*
-------------------------------------------
.*                   # Ignore some garbage in the front
(header_             # Match the start of the file name,
    \d{10,11}_)      #     including the ID (10 - 11 digits)
.*                   # Ignore the type code in the middle
(_.*_\d{8})          # Match some random characters, then an 8-digit date
.*                   # Ignore anything between this and the file extension
(\.\w{3,4})          # Match the file extension, 3 or 4 characters long
.*                   # Ignore the rest of the string


我希望它匹配如下字符串:

str1 = "header_0000000602_t_mc2e1nrobr1a3s55niyrrqvy_20081212[1].doc [Compatibility Mode]"
str2 = "Microsoft PowerPoint - header_00000000076_d_al41zguyvgqfj2454jki5l55_20071203[1].txt"
str3 = "header_00000000076_d_al41zguyvgqfj2454jki5l55_20071203[1]"


捕获组返回如下内容:

$1  =  header_0000000602_
$2  =  _mc2e1nrobr1a3s55niyrrqvy_20081212
$3  =  .doc


如果没有找到文件扩展名,$3 可以为空。 $3 是可选部分,您可以在上面的 str3 中看到。

如果我添加“?”到第三个捕获组“(.\w{3,4})?”的末尾,RegEx 不再为任何字符串捕获 $3。如果我添加“+”而不是“(.\w{3,4})+”,RegEx 将不再捕获 str3,这是意料之中的。

我觉得使用“?”在第三个捕获组的末尾是合适的事情,但它不像我预期的那样工作。我可能对我用来忽略部分字符串的“.*”部分太天真了。


没有按预期工作:

.*(header_\d*_).*(_.*_.{8}).*(\.\w{3,4})?.*

【问题讨论】:

  • 您使用的是哪个正则表达式实现?

标签: .net regex


【解决方案1】:

这是你的正确结果

.*?(header_\d*_).*?(_.*_.{8})[^.]*(\.\w{3,4})?.*
-------------------------------------------
.*?                  # Prevent a greedy match
(header_             # 
    \d{10,11}_)      # 
.*?                  # Prevent a greedy match
(_.*_\d{8})          # 
[^.]*                # Take everything that is NOT a period
(\.\w{3,4})          # Match the extension
.*                   # 

隐含的假设是句点将是数字匹配后文件扩展名的开头。以下不符合此要求:

string unmatched = "header_00000000076_d_al41zguyvgqfj2454jki5l55_20071203[1].foobar.txt"

另外,在 .NET 中取出您的组时,请确保您的代码如下所示:

regex.Match(string_to_match).Groups[1].Value
regex.Match(string_to_match).Groups[2].Value
regex.Match(string_to_match).Groups[3].Value

不是这个:

// 0 index == string_to_match
regex.Match(string_to_match).Groups[0].Value
regex.Match(string_to_match).Groups[1].Value
regex.Match(string_to_match).Groups[2].Value

这件事一开始让我很吃惊。

【讨论】:

  • 感谢您提供有关群组索引的说明。当我开始这个时,我注意到了。
【解决方案2】:

这里有一个适用于您发布的内容:

^.*(?<header>header_\d{10,11})_.*(?<date>_[a-z0-9]+_\d{8})(\[\d+\])(?<ext>(\.[a-zA-Z0-9]{3,4})?).*

替换为:

Header: $1
Date: $2
Extension: $4

我没有在替换中使用命名组,因为我不知道如何让 TextMate 执行此操作,但命名组有助于强制捕获。

【讨论】:

    【解决方案3】:

    这适用于您发布的示例:

    ^.*?(?<header>\d+)_.*?_(?<date>\d{8}).*?(?:\.(?<ext>\w{3,4}))?[\w\s\[\]]*$
    

    我假设文本“标题”以及它与日期之间的随机字符并不重要,因此此正则表达式不会捕获这些字符。为了清楚起见,我还使用了 .NET 命名捕获功能,但请注意,其他风格的 RegEx 不支持它。

    如果文件名后面的文本包含除 [ 和 ] 以外的任何非字母数字字符,则需要修改模式。

    【讨论】:

      【解决方案4】:

      在您的第二次匹配中指定您只想匹配其中没有句点的所有字符然后为您的扩展进行匹配。

      ".*(header_\d{10,11}_).*(_.*_\d{8})[^.]*(\.\w{3,4})?"
      

      【讨论】:

        【解决方案5】:

        一种可能性是倒数第二个.* 是贪婪的。您可以尝试将其更改为:

        .*(header_\d*_).*(_.*_.{8}).*?(\.\w{3,4})?.*
                                     ^ Added that
        

        这是不正确的,这将与您提供的输入相匹配,但它假定它遇到的第一个 . 是文件扩展名的开头:

        .*(header_\d*_).*(_.*_.{8})[^\.]*(\.\w{3,4})?.*
        

        编辑:删除我在第二个正则表达式中的转义。

        【讨论】:

        • 感谢您的建议,但这不会改变我的结果。
        • 好的,肖恩!这是我想出的模式: .*?(header_\d*_).*?(_\d{8})[^\.]*(\.[a-zA-Z0-9]{3 ,4})?
        • @Cerebrus:你的回答也有效。您应该将其发布为答案而不是评论。
        【解决方案6】:

        好吧,.* 可能是启动正则表达式的错误方式-它将匹配任何 (.) 的 0 个或多个 (*) 单个字符...这意味着您的整个文件名将与之匹配独自的。如果您将其关闭,则正则表达式将在达到 header 时开始匹配,这就是您想要的。您也可以将其替换为\w,它与分词匹配。我还建议使用The Regex Coach 之类的工具,这样您就可以逐步检查它,看看到底出了什么问题以及您的捕获组将是什么。

        【讨论】:

        • 如果我将第一个 ".*" 替换为 "\w?",我会得到相同的结果。不过,谢谢你的建议。我将使用“\w?”在我的正则表达式中更清楚。
        • 感谢您提及正则表达式教练。那东西太棒了。
        【解决方案7】:

        我相信问题出在您的第三个.* 上,您在上面用“忽略此文件和文件扩展名之间的任何内容”进行了注释。它是贪婪的,所以它会匹配任何东西。当您将扩展模式设为可选时,第三个 .* 匹配到字符串的末尾,这是允许的。假设在那个无关位中永远不会有 '.' 字符,您可以将 .* 替换为 [^.]*,并且在您恢复您必须删除的 ? 后,其余部分有望工作。

        【讨论】:

        • 确实有效。我想我可以假设在这种情况下,文件扩展名之前永远不会出现一个句点。谢谢!
        猜你喜欢
        • 2013-03-06
        • 1970-01-01
        • 2021-11-15
        • 1970-01-01
        • 1970-01-01
        • 2022-12-03
        • 2017-08-16
        • 1970-01-01
        相关资源
        最近更新 更多