【问题标题】:Superpower: match a string with parser only if it begins a line超能力:仅当字符串开始一行时才将字符串与解析器匹配
【发布时间】:2018-10-08 16:31:02
【问题描述】:

在超能力解析时,如何只匹配一行中的第一个字符串?

例如,我需要匹配“A:Hello Goodbye\n”中的A冒号,而不是“Goodbye A:Hello\n”中的冒号

【问题讨论】:

  • 您是否要解析多行文本,例如“A: Hello Goodbye”?你的预期输出是什么?键/值对,例如Key = "A"Value = "Hello Goodbye" ?另外,您是否希望“再见 A:您好”解析失败?
  • 我想这取决于它是分词器还是解析器。如果分词器(我认为这是更好的解决方案),那么我希望任何与上述正则表达式匹配的东西都是一个令牌。
  • 这真的取决于你的预期输出。你想从中提取什么数据?
  • 根据上下文,语言中的每个命令都是单行(以换行符结束),某些字符/字符串如果开始行则具有特殊含义,但如果它们出现则没有之后。因此,如果它发生在解析器中,那么它可能会返回一个包含字符串“A:”的 Actor 对象,然后是一个包含字符串“Hello Goodbye”的 FreeText 对象。在第二种情况下,整个事情都是 FreeText("Goodbye A: Hello") 因为 Actor 解析器会失败。
  • 我想我明白了,但是要构建这样的解析器,您需要提供一个更全面的示例。您能否更新问题以包含该问题以及您希望将输出解析为的类?

标签: c# tokenize superpower


【解决方案1】:

使用您的示例 here,我会将您的 ActorParserNodeParser 定义更改为:

public readonly static TokenListParser<Tokens, Node> ActorParser =
    from name in NameParser
    from colon in Token.EqualTo(Tokens.Colon)
    from text in TextParser
    select new Node {
        Actor = name + colon.ToStringValue(),
        Text = text
    };

public readonly static TokenListParser<Tokens, Node> NodeParser =
    from node in ActorParser.Try()
        .Or(TextParser.Select(text => new Node { Text = text }))
    select node;

我觉得 Superpower 有一个错误,因为我不知道为什么在 NodeParser 中我必须在第一个解析器上放置一个 Try(),当它与 Or() 链接时,但它会抛出如果我没有添加它会出错。

此外,您在检查 input[1] 时的验证不正确(可能只是复制粘贴问题)。它应该检查 "Goodbye A: Hello" 而不是 "Hello A: Goodbye"

【讨论】:

  • 感谢您的更新。我接受了这一点,尽管我意识到我真正需要的是标记器版本,我已经 posted here 以及测试用例......
【解决方案2】:

除非设置了RegexOptions.Multiline,否则^ 匹配字符串的开头,无论它是否位于行首。

你大概可以使用内联(?m)来开启多行:

static TextParser<Unit> Actor { get; } =
  from start in Span.Regex(@"(?m)^[A-Za-z][A-Za-z0-9_]+:")
  select Unit.Value;

【讨论】:

  • 传递 RegexOptions.Multiline 选项并不能解决问题:Span.Regex(@"^[A-Za-z][A-Za-z0-9_]*:", RegexOptions。多行)
  • 嗯——如果多行不能解决它,那么您收到的Span 很可能是一个不是您认为的切片(不对应于一行)。尝试破坏您的代码并检查跨度。如果这不能解决您的问题,请发布一个演示失败的最小工作示例,以便我们运行它并帮助您解决问题。
  • 好的,看来如果该行是“1 abc:”并且设置了 Ignore(Span.WhiteSpace),则标记器使用第一个标记 ('1'),然后忽略空格按照指示,然后将“abc:”视为从位置 0 开始,因此匹配。但我想要的是只匹配“abc:”,如果它是第一个令牌......怎么做?
  • 您不能从 inside 标记器执行此操作,因为它只能在处理了之前的标记后看到剩余部分。如果您在更高级别上解释更多您正在尝试做的事情,并举​​例说明您期望的完整输入和您想要完成的确切行为,可能会有所帮助。标记化的行为根据规则将输入分解为多个标记;如果你想选择一个特定的标记,你可以在标记器完成后这样做。
  • 您能否发布一个最小的演示程序,该程序可以编译并执行以展示您所描述的行为?
【解决方案3】:

我实际上做过类似的事情,但我没有使用 Tokenizer。

private static string _keyPlaceholder;

private static TextParser<MyClass> Actor { get; } =
    Span.Regex("^[A-Za-z][A-Za-z0-9_]*:")
        .Then(x =>
             {
                 _keyPlaceholder = x.ToStringValue();
                 return Character.AnyChar.Many();
             }
         ))
    .Select(value => new MyClass { Key = _keyPlaceholder, Value = new string(value) });

这个我没有测试,只是凭记忆写出来的。上面的解析器应该有以下内容:

myClass.Key = "A:"
myClass.Value = " Hello Goodbye"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-22
    • 1970-01-01
    • 2017-08-09
    • 2013-02-11
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多