如何在此RegEx中指定可选的捕获组?

时间:2009-01-28 17:45:19

标签: .net regex

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

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


我的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})+”,正则表达式不再捕获STR3在所有,这是可以预料的。

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

不按预期工作:

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

7 个答案:

答案 0 :(得分:5)

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

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

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

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

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

答案 1 :(得分:3)

我认为问题发生在您的第3个.*中,您在上面注释了“忽略此文件扩展名之间的任何内容”。这很贪心,所以它会与任何东西相匹配。当您将扩展模式设置为可选时,第3个.*将匹配字符串的末尾,这是允许的。假设在该无关位中永远不会有“.”字符,您可以将.*替换为[^.]*,其余的将在您恢复?之后有效你必须删除。

答案 2 :(得分:2)

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

答案 3 :(得分:2)

在第二场比赛中指定您只想匹配其中没有句号的所有字符然后为您的分机匹配。

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

答案 4 :(得分:2)

这是你的正确结果

.*?(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

这首先让我感到沮丧。

答案 5 :(得分:1)

这适用于您发布的示例:

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

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

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

答案 6 :(得分:1)

这是适用于你发布的内容的一个:

^.*(?<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这样做,但命名组有助于强制捕获。