需要在 C# 正则表达式中提取文本

Posted

技术标签:

【中文标题】需要在 C# 正则表达式中提取文本【英文标题】:Need to extract text in a C# Regex 【发布时间】:2021-08-21 18:35:34 【问题描述】:

我有一个字符串集合,例如Some song [FEAT. John Doe],我正在尝试提取“特色”部分。它可以通过几个不同的词之一来标识"FEAT|FEAT\\.|Featuring",并且可以用括号括起来也可以不括起来。我正在为此使用正则表达式,这就是我到目前为止所得到的:

[TestMethod]
public void ExtractFeaturedPerformers()

    IEnumerable<string> titles = new string[]
    
        "abc [FEAT one two] 123",
        "def(FEAT. three'four) 456",
        "ghi Featuring five",
        "jkl"
    ;

    // Must be able to use an arbitrary array of words
    var arrayOfWords = new string[]  "FEAT", "FEAT.", "Featuring" ;
    string options = string.Join("|", arrayOfWords.Select(s => Regex.Escape(s)));
    var result = new List<string>();

    foreach(string title in titles)
    
        var _ = Regex.Match(title, $@"(?<=(options)\s*)(.*?)(?=[\]\)]|$)");
        if (_.Success)
            result.Add(_.Value);
    

    Assert.AreEqual(3, result.Count());
    Assert.IsTrue(result.Contains("one two"));
    Assert.IsTrue(result.Contains("three'four"));
    Assert.IsTrue(result.Contains("five"));

我大部分时间都在工作,但有两个限制。我的主要问题是第二个结果包括.

. three'four

如何将其作为 Regex 的一部分删除,以便我可以接受任意的 options 字符串,而不是稍后将其剥离?处理. 是我主要关心的问题,但我也很感激从结果中删除前导和尾随空格的建议,这样我以后就不必再调用Trim()了。

【问题讨论】:

【参考方案1】:

你需要

(?:FEAT\.?|Featuring)\s*([^])]*)

见regex demo

详情

(?:FEAT\.?|Featuring) - FEAT 和可选的 .Featuring \s* - 零个或多个空格 ([^])]*) - 正在捕获组 1:除 ]) 之外的零个或多个字符。

您需要修改 C# 代码以获取第 1 组值。

这里是full C# demo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class Test

    public static void Main()
    
        IEnumerable<string> titles = new string[]
        
            "abc [FEAT one two] 123",
            "def(FEAT. three'four) 456",
            "ghi Featuring five",
            "jkl"
        ;

        var keys = new List<string>  "FEAT", "FEAT.", "Featuring" ;
        keys = keys.OrderByDescending(x => x.Length).ToList();
        var pattern = $@"(?:string.Join("|", keys.Select(z => Regex.Escape(z))))\s*([^])]*)";
        Console.WriteLine(pattern);
        
        var result = new List<string>();
        foreach(string title in titles)
        
            var _ = Regex.Match(title, pattern);
            if (_.Success)
                result.Add(_.Groups[1].Value);
        
    
        Console.WriteLine( result.Count()); // Assert.AreEqual(3, result.Count());
        Console.WriteLine( result.Contains("one two") ); //Assert.IsTrue(result.Contains("one two"));
        Console.WriteLine( result.Contains("three'four") ); //Assert.IsTrue(result.Contains("three'four"));
        Console.WriteLine( result.Contains("five") ); // Assert.IsTrue(result.Contains("five"));
    

输出是

(?:Featuring|FEAT.|FEAT)\s*([^])]*)
3
True
True
True

注意正则表达式模式是如何构建的:

var keys = new List&lt;string&gt; "FEAT", "FEAT.", "Featuring" ; 使用搜索短语初始化 keys 字符串列表 keys = keys.OrderByDescending(x =&gt; x.Length).ToList(); - 按长度降序对列表中的项目进行排序 var pattern = $@"(?:string.Join("|", keys.Select(z =&gt; Regex.Escape(z))))\s*([^])]*)"; - 通过将转义的搜索短语放入非捕获组中创建正则表达式模式,其中| 交替运算符(?:Featuring|FEAT\.|FEAT)

【讨论】:

为什么要放弃 Lookbehind 解决方案? @41686d6564 因为后视中的可选. 很麻烦。由于正则表达式引擎从左到右搜索匹配项,FEAT 之前的位置会更早找到,并且匹配项中仍会出现点。使用消费模式时,可选点将被消费,并且不会出现在 Group 1 值中。如果Feat 部分之后总是有一个空格,(?&lt;=(?:FEAT\.?|Featuring)\s)([^])]*) 可能会起作用。在这些情况下,捕获机制更安全。 啊,我总是忘记 Lookbehinds 的这个警告。谢谢你,维克托。我站得更正。 :) 感谢您的回答 Wiktor。它适用于 FEATFEAT.Featuring。唯一的麻烦是,我需要能够接受任意字符串。 @Connell.O'Donnell 你的意思是像string options = string.Join("|", collectionOfWords.Select(s =&gt; Regex.Escape(s)));吗?

以上是关于需要在 C# 正则表达式中提取文本的主要内容,如果未能解决你的问题,请参考以下文章

C# 正则表达式提取html中的文本

C# 正则表达式提取指定文本内的内容

C# 正则表达式提取指定文本内的内容

C# 正则表达式提取指定文本内的内容

C# 正则表达式提取指定文本内的内容

c# 正则表达式提取()中的值