【问题标题】:Regex tokenize issue正则表达式标记化问题
【发布时间】:2016-03-04 07:35:41
【问题描述】:

我有用户输入的字符串,并希望对它们进行标记。为此,我想使用正则表达式,但现在遇到了一个特殊情况的问题。 一个示例字符串是

Test + "Hello" + "Good\"more" + "Escape\"This\"Test" 或 C# 等价物

@"Test + ""Hello"" + ""Good\""more"" + ""Escape\""This\""Test"""

我可以匹配 Test 和 + 标记,但不能匹配 "。我使用 " 让用户指定这实际上是一个字符串而不是特殊标记。现在如果用户想在字符串中使用 " 字符,我想允许他用 \ 转义它。

所以规则是:给我两个“”之间的所有内容,但最后一个“”前面的字符不能是\。

我期望的结果是:"Hello""Good\"more""Escape\"This\"Test" 我需要 " " 字符出现在最后一场比赛中,所以我知道这是一个字符串。

我目前有正则表达式@"""([\w]*)(?<!\\"")""",它给了我以下结果:"Hello""more""Test"

所以后面的样子并没有像我想要的那样工作。有谁知道获取我想要的字符串的正确方法吗?

【问题讨论】:

标签: c# regex tokenize


【解决方案1】:

这是我用来解析命令行的正则表达式的改编:

(?!\+)((?:"(?:\\"|[^"])*"?|\S)+)

例如here at regex101

(适应是忽略+并检查\"而不是""的负面预测)

希望对你有所帮助。

问候。

编辑:

如果您对周围的引号不感兴趣:

(?!\+)(?:"((?:\\"|[^"])*)"?|(\S+))

【讨论】:

【解决方案2】:

为了更安全,我建议使用以下正则表达式获取未转义的 "..." 对中的所有子字符串:

^(?:[^"\\]*(?:\\.[^"\\]*)*("[^"\\]*(?:\\.[^"\\]*)*"))+

匹配

  • ^ - 字符串的开头(这样我们就可以检查每个 " 和转义序列)
  • (?: - 非捕获组 1 作为后续子模式的容器
    • [^"\\]*(?:\\.[^"\\]*)* - 匹配除 " 和 \ 之外的 0+ 个字符,后跟 0+ 个 \\. 序列(任何转义序列),然后匹配除 " 和 \ 之外的 0+ 个字符(因此,我们避免匹配第一个被转义的",它前面可以有任意数量的转义序列)
    • ("[^"\\]*(?:\\.[^"\\]*)*") - 捕获匹配 "..." 子字符串的组 1,其中可能包含任何转义序列
  • )+ - 重复 1 次或多次的第一个非捕获组的结尾

查看regex demo,这里是C# demo:

var rx = "^(?:[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"))+";
var s = @"Test + ""Hello"" + ""Good\""more"" + \""Escape\""This\""Test\"" + ""f""";
var matches = Regex.Matches(s, rx)
        .Cast<Match>()
        .SelectMany(m => m.Groups[1].Captures.Cast<Capture>().Select(p => p.Value).ToArray())
        .ToList();
Console.WriteLine(string.Join("\n", matches));

更新

如果您需要删除标记,只需使用以下代码匹配并捕获它们之外的所有标记:

var keep = "[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*";
var rx = string.Format("^(?:(?<keep>{0})\"{0}\")+(?<keep>{0})$", keep);
var s = @"Test + ""Hello"" + ""Good\""more"" + \""Escape\""This\""Test\"" + ""f""";
var matches = Regex.Matches(s, rx)
        .Cast<Match>()
        .SelectMany(m => m.Groups["keep"].Captures.Cast<Capture>().Select(p => p.Value).ToArray())
        .ToList();
Console.WriteLine(string.Join("", matches));

见another demo

输出:Test + + + \"Escape\"This\"Test\" + 对应 @"Test + ""Hello"" + ""Good\""more"" + \""Escape\""This\""Test\"" + ""f""";。

【讨论】:

  • 看起来很不错。但是,对于标记化,我想用空字符串替换输入字符串中的匹配项,因为我有多个用于不同目的的表达式。您的正则表达式将整个字符串作为匹配项提供给我,那么删除匹配项最安全的方法是什么?我可以替换正则表达式找到的每个字符串的第一次出现,但我想知道这是否安全。
  • 好的,所以您需要删除"Hello"、"Good\"more" 和"f",对吗?让我检查一下……
猜你喜欢
  • 2012-04-02
  • 2011-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多