标记化中的奇怪的正则表达式行为

时间:2016-04-15 22:03:25

标签: c# regex tokenize

我正在使用以下正则表达式进行标记:

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

正则表达式应该在以后过滤掉所有内容,但是我遇到问题的输入字符串格式是以下形式:

; "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

的情况下删除它们

1 个答案:

答案 0 :(得分:1)

根据我的经验,在正则表达式比赛中分裂几乎总是不是最好的主意。通过简单匹配,您将获得更好的结果。

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

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

Demo - 当然使用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"

看看处理这些结果比使用拆分更容易吗?