匹配正则表达式中的 Unicode 字符

Posted

技术标签:

【中文标题】匹配正则表达式中的 Unicode 字符【英文标题】:Matching Unicode characters in a regular expression 【发布时间】:2021-10-16 23:20:14 【问题描述】:

我使用 HttpClient 类从网站检索字符串。网络服务器以 UTF-8 编码发送它们。字符串的格式为abc | a,如果它们位于字符串的末尾,我想删除管道、空格和空格后面的字符。

sText = Regex.Replace (sText, @"\| .$", "");

按预期工作。现在,在某些情况下,管道和空格后面跟着另一个字符,例如笑脸。该字符串的格式为abc | ????。上面的正则表达式不起作用,我必须使用

sText = Regex.Replace (sText, @"\| ..$", "");

改为(两个点)。

我很确定这与编码以及笑脸在 UTF-8 中使用的字节数比拉丁字符多的事实有关 - 以及 c# 不知道编码的事实。笑脸只是一个字符,即使它使用更多字节,所以在告诉 c# 正确的编码(或转换字符串)后,第一个正则表达式应该适用于两种情况。

如何做到这一点?

【问题讨论】:

在 .NET 中将表情符号与正则表达式匹配存在很大问题,因为没有 \pEmoji 构造。您所能做的就是定义regex for any emoji 或任何字节(.)。或者,如果您知道字符串中没有出现哪种字符并使用它来构建字符串模式的结尾,您可以解决它。 Wiktor @Magnetron(几乎)在他(不公平地被否决)已删除的答案中是正确的。 Regex.Replace(sText, @"\| (\pCs2|.)$", ""); 应该作为 .NET 中的内部编码为 UTF-16 并且 BMP 之上的所有字符始终是两个代理项...... 笑脸只是一个例子。我想删除 看起来 像一个项目的所有内容(一个字符、一个数字、一个符号......)。 \pCs2 可能太有限了。 【参考方案1】:

就像 cmets 中建议的那样,这个问题很难使用 Regex 解决。你所说的“看起来像一个项目”实际上是一个grapheme cluster。对应的 .NET 术语是一个“文本元素”,可以通过使用 StringInfo.GetTextElementEnumerator 进行解析和迭代。

基于文本元素的可能解决方案非常简单:我们只需要从输入字符串中提取最后 3 个文本元素,并确保它们引用管道、空格,最后一个可以是任意的。请在下面找到建议的方法实施。

void Main()

    var inputs = new[] 
        "abc | a",
        "abc | ab", // The only that shouldn't be trimmed
        "abc | ?",
        "abc | " + "\uD83D\uDD75\u200D\u2642\uFE0F" // "man-detective" (on Windows)
    ;
    
    foreach (var input in inputs)
    
        var res = TrimTrailingTextElement(input);

        Console.WriteLine("Input : " + input);
        Console.WriteLine("Result: " + res);
        Console.WriteLine();
    


string TrimTrailingTextElement(string input)

    // A circular buffer for storing the last 3 text elements
    var lastThreeElementIdxs = new int[3]  -1, -1, -1 ;
    
    // Get enumerator of text elements in the input string
    var enumerator = StringInfo.GetTextElementEnumerator(input);

    // Iterate through the enitre input string,
    // at each step save to the buffer the current element index
    var i = -1;
    while (enumerator.MoveNext())
    
        i = (i + 1) % 3;
        lastThreeElementIdxs[i] = enumerator.ElementIndex;
    

    // The buffer index must be positive for a non-empty input
    if (i >= 0)
    
        // Extract indexes of the last 3 elements
        // from the circular buffer
        var i1 = lastThreeElementIdxs[(i + 1) % 3];
        var i2 = lastThreeElementIdxs[(i + 2) % 3];
        var i3 = lastThreeElementIdxs[i];

        if (i1 >= 0 && i2 >= 0 && i3 >= 0 && // All 3 indexes must be initialized
            i3 - i2 == 1 && i2 - i1 == 1 &&  // The 1 and 2 elements must be 1 char long
            input[i1] == '|' &&              // The 1 element must be a pipe 
            input[i2] == ' ')                // The 2 element must be a space
        
            return input.Substring(0, i1);
        
    
    
    return input;

【讨论】:

以上是关于匹配正则表达式中的 Unicode 字符的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式

C# 正则表达式匹配表情符号

正则表达式日记

正则表达式学习笔记

正则表达式之匹配汉字

Unicode 在 UtralEdit 正则表达式 如何表达,或者匹配汉字...