标记化中奇怪的regex行为

本文关键字:regex 行为 | 更新日期: 2024-09-19 15:43:03

我正在使用以下正则表达式来标记化:

reg = new Regex("([ ''t{}%$^&*():;_–`,''-''d!'"?'n])");

regex应该稍后过滤掉所有内容,但是我遇到问题的输入字符串格式如下:

; "string1"; "string2"; "string...n";

据我所知,字符串; "social life"; "city life"; "real life"的结果应该如下所示:

; White " social White life " ; White " city White life " ;  White " real White life "

然而,有一个问题,我得到了以下形式的输出

; empty White empty " social White life " empty ; empty White empty " city White life " empty ; empty White empty " real White life " empty

白色:表示空白,empty:表示拆分数组中的空条目。

我的拆分代码如下:

string[] ret = reg.Split(input);
 for (int i = 0; i < ret.Length; i++)
        {
            if (ret[i] == "")
                Response.Write("empty<br>");
            else
                if (ret[i] == " ")
                    Response.Write("White<br>");
                else
                    Response.Write(ret[i] + "<br>");
        }

为什么我收到这些空条目?尤其是当;后面跟着空间,后面跟着"时,结果看起来如下:

; empty White empty "

我能得到命令为什么添加空条目的解释吗?以及如何在没有任何额外的O(n)复杂性或使用另一种数据结构如ret 的情况下删除它们

标记化中奇怪的regex行为

根据我的经验,在regex匹配中进行拆分几乎总是不是最好的主意。通过简单匹配,你会得到更好的结果。

正则表达式非常适合用于标记化目的,因为它们可以让您非常容易地实现状态机,只需看看:

'G(?:
  (?<string> "(?>[^"'']+|''.)*" )
| (?<separator> ; )
| (?<whitespace> 's+ )
| (?<invalid> . )
)

演示-当然可以与RegexOptions.IgnorePatternWhitespace一起使用。

在这里,每个匹配都将具有以下属性:

  • 它将从上一场比赛结束时开始,因此不会有不匹配的文本
  • 它将包含恰好一个匹配组
  • 组的名称告诉您令牌类型
  • 您可以忽略whitespace组,如果遇到匹配的invalid组,则应该引发错误

string组将匹配整个带引号的字符串,它可以处理字符串中的转义,如'"

invalid组应该始终是模式中的最后一个。您可以为其他类型添加规则。

一些示例代码:

var regex = new Regex(@"
    'G(?:
      (?<string> ""(?>[^""'']+|''.)*"" )
    | (?<separator> ; )
    | (?<whitespace> 's+ )
    | (?<invalid> . )
    )
", RegexOptions.IgnorePatternWhitespace);
var input = "; '"social life'"; '"city life'"; '"real life'"";
var groupNames = regex.GetGroupNames().Skip(1).ToList();
foreach (Match match in regex.Matches(input))
{
    var groupName = groupNames.Single(name => match.Groups[name].Success);
    var group = match.Groups[groupName];
    Console.WriteLine("{0}: {1}", groupName, group.Value);
}

这会产生以下内容:

separator: ;
whitespace:
string: "social life"
separator: ;
whitespace:
string: "city life"
separator: ;
whitespace:
string: "real life"

看看处理这些结果比使用拆分容易多少?