【问题标题】:Pattern matching with extraction of found substrings to variables将找到的子字符串提取到变量的模式匹配
【发布时间】:2015-02-05 14:38:44
【问题描述】:

我有一些已定义格式的字符串,例如Foo.<Whatever>.$(Something),我想将它们分成几部分,并让每个部分自动分配给一个变量。

我曾经用 C# 类和运算符重载编写了类似于 bash/shell 管道命令选项“

ParseExpression ex = pex("item1") > ".<" > pex("item2") > ">.$(" > pex("item3") > ")";
ParseResult r = new ParseResult(ex, "Foo.<Whatever>.$(Something)");

ParseResult 然后有一个字典,其中键 item1 到 item3 设置为在给定字符串中找到的字符串。方法pex 生成了一些可以与> 运算符一起使用的对象,最终具有构成ParseExpression 的ParseExpressionParts 链。

目前我手头没有代码,在我再次从头开始编写代码之前,我想我最好问问是否有人已经完成并发布了它。

【问题讨论】:

  • 这应该可以通过一个简单的正则表达式来实现,类似于^(\w+)\.&lt;(\w+)&gt;\.\$\((\w+)\)$,它将产生 Foo、Whatever 和 Something 作为组 1、2 和 3。不需要解析器。
  • 是否有一些漂亮的库可以更容易地构造正则表达式,形式如下:Regex r = RegexBuilder.AnyChar().FollowedBy('&lt;').AnyChar().FollowedBy('&gt;')...
  • 我建议“咬紧牙关”并学习正则表达式语法。是的,它是一种非常晦涩的语言,但是(与其他晦涩的语言相反)它非常有用并且(或多或少)标准化。从长远来看,这是一项一次性投资。

标签: c# string parsing pattern-matching


【解决方案1】:

解析表达式让我想起了parser combinators,比如 Parsec 和 FParsec(对于 F#)。语法有多复杂?事实上,它可以由带有组的正则表达式处理。

如果您想使用解析器组合器创建更复杂的语法,您可以使用FParsec,它是更广为人知的解析器组合器之一,针对 F#。通常,在这种情况下会大量使用 F# 等函数式语言。 CSharp-monad 是一个针对 C# 的解析器组合器。不过这个项目不是很活跃。

您还可以使用成熟的解析器生成器,例如 ANTLR 4。 ASP.NET MVC 使用 ANTLR 来解析 Razor 语法视图。 ANTLR 4 创建了一个解析树,并允许您使用访问者或侦听器来处理它,这类似于 DOM 或 SAX 处理。只要遇到一个元素,侦听器就会调用您的代码(例如开头&lt;,内容等),而访问者在完成的树上工作。

ANTLR 的 Visual Studio 扩展将为您的语法生成解析器类以及基本的访问者和侦听器类。基于 NetBeans 的 ANTLRWorks IDE 使创建和测试语法变得非常容易。

您的示例的粗略语法是:

format: tag '.' '<' category '>' '.' '$' '(' value ')';
tag      : ID;
category : ID;
value    : ID;

ID       :[A-Z0-9]+;

或者你可以定义像FOO : 'FOO' 这样对你的语法有特殊意义的关键字。访问者或侦听器可以处理标签,例如格式化字符串、对值执行操作等。

没有硬性规定。就个人而言,我将正则表达式用于更简单的情况,例如处理相对简单的日志文件,而 ANTLR 用于更复杂的情况,例如屏幕抓取大型机数据。我没有研究过解析器组合器,因为我从来没有时间熟悉 F#。虽然处理一些混乱的 log4net 日志文件,但它们会非常方便

【讨论】:

    【解决方案2】:

    我从 Heinzi 的建议开始,最终想出了以下代码:

    const string tokenPrefix = "px";
    const string tokenSuffix = "sx";
    const string tokenVar = "var";
    
    string r = string.Format(@"(?<{0}>.*)\$\((?<{1}>.*)\)(?<{2}>.*)",
                             tokenPrefix, tokenVar, tokenSuffix);
    Regex regex = new Regex(r);
    Match match = regex.Match("Foo$(Something)Else");
    
    if (match.Success)
    {
      string prefix = match.Groups[tokenPrefix].Value; // = "Foo"
      string suffix = match.Groups[tokenSuffix].Value; // = "Something"
      string variable = match.Groups[tokenVar].Value;  // = "Else" 
    }
    

    在与一位同事讨论此事后,有人告诉我,当我的模式使用量增加并且我想要更好的可维护性时,我被告知考虑使用名为 "Sprache" 的 C# 解析器构造库(它介于正则表达式和类似 ANTLR 的工具集之间)。

    【讨论】:

      猜你喜欢
      • 2015-10-19
      • 2010-10-27
      • 1970-01-01
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 1970-01-01
      相关资源
      最近更新 更多