正则表达式模式无法匹配某些节目标题。

6

使用C#正则表达式从字符串中匹配并返回解析的数据会导致不可靠的结果。

我正在使用以下模式:

Regex r=new Regex( 
      @"(.*?)S?(\d{1,2})E?(\d{1,2})(.*)|(.*?)S?(\d{1,2})E?(\d{1,2})",
      RegexOptions.IgnoreCase
);

以下是几个失败的测试用例。
Ellen 2015.05.22 Joseph Gordon Levitt [REPOST]
The Soup 2015.05.22 [mp4]
Big Brother UK Live From The House (May 22, 2015)

应该返回

  • 显示名称(例如,Ellen
  • 日期(例如,2015.05.22
  • 额外信息(例如,Joseph Gordon Levitt [REPOST]

Alaskan Bush People S02 Wild Times Special

应该返回

  • 显示名称(例如,阿拉斯加荒野之人
  • 季节(例如,02
  • 额外信息(例如,狂野时代特别节目

500 Questions S01E03

应该返回

  • 节目名称(例如,500 Questions
  • 季节(例如,01
  • 集数(例如,03

有效并返回正确数据的示例

Boyster S01E13 – E14
Mysteries at the Museum S08E08
Mysteries at the National Parks S01E07 – E08
The Last Days Of… S01E06
Born Naughty? S01E02
Have I Got News For You S49E07

看起来,这个模式如果没有找到S和E,就会忽略它们,然后使用第一组匹配的数字填充该位置。

很明显,这个模式需要更多的工作才能与上述变化的字符串配合使用。非常感谢您在此事上的帮助。


为什么你写了两次相同的模式?@"(.*?)S?(\d{1,2})E?(\d{1,2})(.*)|(.*?)S?(\d{1,2})E?(\d{1,2})" - karthik manchala
这与之前的模式不同。请注意,一个以(.*)结尾以匹配任何尾随字符,而另一个则没有。我发现如果去掉(.*),那些在剧集编号后面有更多字符的字符串根本无法被捕获。 - Kraang Prime
我所说的是第二部分是第一部分的子集,其中.*可以匹配零个字符..?? - karthik manchala
我希望您能重新表述一下您的问题,因为似乎您正在尝试使用通配符和单个正则表达式来捕获多种模式。我建议您展示一个确切的输入示例,以便进行正则表达式匹配。同时,考虑到输入非常多样化,您可能需要多个模式,并且可能需要多次解析文本。 - OMG-1
避免使用 '.',它将取整行直至结束。你需要更多的或来处理日期。使用命名组来处理空组。这里是我的修正: @"(?'name'[^S])?S(?'season'\d{1,2})E?(?'episode'\d{1,2})?(?'end'[^$])|(?'name'[^S])?S(?'season'\d{1,2})E(?'episode'\d{1,2})" - jdweng
2个回答

5

分而治之

您试图使用一个简单的表达式解析过多的内容。这样做不会很好地工作。在这种情况下,最好的方法是将问题分解成较小的问题,并分别解决每个问题。然后,我们可以稍后将所有内容合并为一个模式。

让我们为您想要提取的数据编写一些模式。

  • Season/episode:

    S\d+(?:E\d+(?:\s*\p{Pd}\s*E\d+)?)?
    

    I used \p{Pd} instead of - to accommodate for any dash type.

  • Date:

    \d{4}\.\d{1,2}\.\d{1,2}
    

    Or...

    (?i:January|February|March|April|May|June|July|August|September|October|November|December)
    \s*\d{1,2},\s*\d{4}
    
  • Write a simple pattern for extra info:

    .*?
    

    (yeah, that's pretty generic)

  • We can also detect the show format like this:

    \[.*?\]
    
  • You can add additional parts as required.

现在,我们可以将所有内容放进一个模式中,使用组名称来提取数据:
^\s*
(?<name>.*?)
(?<info> \s+ (?:
    (?<episode>S\d+(?:E\d+(?:\s*\p{Pd}\s*E\d+)?)?)
    |
    (?<date>\d{4}\.\d{1,2}\.\d{1,2})
    |
    \(?(?<date>(?i:January|February|March|April|May|June|July|August|September|October|November|December)\s*\d{1,2},\s*\d{4})\)?
    |
    \[(?<format>.*?)\]
    |
    (?<extra>(?(info)|(?!)).*?)
))*
\s*$

只需要忽略 info 组 (它被用于在 extra 中的条件语句上,这样 extra 不会消耗应该属于展示名称的部分)。并且你可以得到多个 extra 信息,所以只需将它们连接起来,每个部分之间加一个空格。

示例代码:

var inputData = new[]
{
    "Boyster S01E13 – E14",
    "Mysteries at the Museum S08E08",
    "Mysteries at the National Parks S01E07 – E08",
    "The Last Days Of… S01E06",
    "Born Naughty? S01E02",
    "Have I Got News For You S49E07",
    "Ellen 2015.05.22 Joseph Gordon Levitt [REPOST]",
    "The Soup 2015.05.22 [mp4]",
    "Big Brother UK Live From The House (May 22, 2015)",
    "Alaskan Bush People S02 Wild Times Special",
    "500 Questions S01E03"
};

var re = new Regex(@"
    ^\s*
    (?<name>.*?)
    (?<info> \s+ (?:
        (?<episode>S\d+(?:E\d+(?:\s*\p{Pd}\s*E\d+)?)?)
        |
        (?<date>\d{4}\.\d{1,2}\.\d{1,2})
        |
        \(?(?<date>(?i:January|February|March|April|May|June|July|August|September|October|November|December)\s*\d{1,2},\s*\d{4})\)?
        |
        \[(?<format>.*?)\]
        |
        (?<extra>(?(info)|(?!)).*?)
    ))*
    \s*$
", RegexOptions.IgnorePatternWhitespace);

foreach (var input in inputData)
{
    Console.WriteLine();
    Console.WriteLine("--- {0} ---", input);

    var match = re.Match(input);
    if (!match.Success)
    {
        Console.WriteLine("FAIL");
        continue;
    }

    foreach (var groupName in re.GetGroupNames())
    {
        if (groupName == "0" || groupName == "info")
            continue;

        var group = match.Groups[groupName];
        if (!group.Success)
            continue;

        foreach (Capture capture in group.Captures)
            Console.WriteLine("{0}: '{1}'", groupName, capture.Value);
    }
}

这个的输出结果是...
--- Boyster S01E13 - E14 ---
name: 'Boyster'
episode: 'S01E13 - E14'

--- Mysteries at the Museum S08E08 ---
name: 'Mysteries at the Museum'
episode: 'S08E08'

--- Mysteries at the National Parks S01E07 - E08 ---
name: 'Mysteries at the National Parks'
episode: 'S01E07 - E08'

--- The Last Days Ofâ?¦ S01E06 ---
name: 'The Last Days Ofâ?¦'
episode: 'S01E06'

--- Born Naughty? S01E02 ---
name: 'Born Naughty?'
episode: 'S01E02'

--- Have I Got News For You S49E07 ---
name: 'Have I Got News For You'
episode: 'S49E07'

--- Ellen 2015.05.22 Joseph Gordon Levitt [REPOST] ---
name: 'Ellen'
date: '2015.05.22'
format: 'REPOST'
extra: 'Joseph'
extra: 'Gordon'
extra: 'Levitt'

--- The Soup 2015.05.22 [mp4] ---
name: 'The Soup'
date: '2015.05.22'
format: 'mp4'

--- Big Brother UK Live From The House (May 22, 2015) ---
name: 'Big Brother UK Live From The House'
date: 'May 22, 2015'

--- Alaskan Bush People S02 Wild Times Special ---
name: 'Alaskan Bush People'
episode: 'S02'
extra: 'Wild'
extra: 'Times'
extra: 'Special'

--- 500 Questions S01E03 ---
name: '500 Questions'
episode: 'S01E03'

会测试一下。谢谢。 - Kraang Prime
根据问题中提供的信息,您返回了我所需的内容。我遇到了另一个问题,也许您可以解决一下(这会帮助我更好地理解分组)>>“Jimmy Fallon 2015 05 22 Sting and Kevin Connolly”。我尝试为此日期添加一个选项,但不确定是否正确 :) - Kraang Prime
当然,你可以直接添加:(?<date>\d{4}[ ]\d{1,2}[ ]\d{1,2}) 或者 (?<date>\d{4}\s\d{1,2}\s\d{1,2}),或者甚至将 (?<date>\d{4}\.\d{1,2}\.\d{1,2}) 改为 (?<date>\d{4}[. ]\d{1,2}[. ]\d{1,2}),但是最后一个选项也会接受 2015 05.22 - 你可以选择最好的变体。 - Lucas Trzesniewski

1

试试这个:

(?<name>.*?)(?:S(?<season>\d{1,2}))?(?:E(?<episode>\d{1,2}))?(?<date>\d{4}\.\d{2}\.\d{2})(?<extra>.*)?

会尝试一下。谢谢。 - Kraang Prime
该方法未返回正确的结果。谢谢尝试 ;) - Kraang Prime

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接