【问题标题】:Extracing specific tags from arbitrary plain text从任意纯文本中提取特定标签
【发布时间】:2016-08-30 23:13:26
【问题描述】:

我想解析纯文本 cmets 并在其中查找某些标签。我正在寻找的标签类型如下所示:

<name#1234>

其中“name”是一个 [a-z] 字符串(来自固定列表),“1234”代表一个 [0-9]+ 数字。这些标签可以在字符串中出现零次或多次,并被任意其他文本包围。例如,以下字符串都是有效的:

"Hello <foo#56> world!"
"<bar#1>!"
"1 &lt; 2"
"+<baz#99>+<squid#0> and also<baz#99>.\n\nBy the way, maybe <foo#9876>"

以下字符串均无效:

"1 < 2"
"<foo>"
"<bar#>"
"Hello <notinfixedlist#1234>"

最后一个无效,因为“notinfixedlist”不是受支持的命名标识符。

例如,我可以使用简单的正则表达式轻松解析它(为了简单起见,我省略了命名组):

<[a-z]+#\d+>

或直接指定固定列表:

<(foo|bar|baz|squid)#\d+>

但我想使用 antlr 有几个原因:

  • 我希望任何与该格式不匹配的内容都会导致解析错误,因此如果文本包含“”但与模式不匹配,则会失败。这些字符必须转义为“<”和“>”如果它不是标签,则分别。
  • 将来我可能会扩展它以支持其他类型的模式(例如:“{foo+666}”或“[[@1234]]”,并希望避免正则表达式语句的爆炸式增长。具有单一语法我可以扩展的文件会很棒。
  • 我喜欢 antlr4 实现了访问者模式,当遇到特定类型的标签时我的代码会被调用,而不必将不同的正则表达式组合在一起。

如何使用 antlr4 实现这样的语法?我见过的大多数示例都是针对整个文本遵循精确规则的语言,而我只希望语法适用于任意文本中的匹配模式。

我想出了这个,我认为这是正确的:

grammar Tags;

parse 
    : ( tag | text )*
    ;

tag 
    : '<' fixedlist '#' ID '>'
    ;

fixedlist 
    : 'foo' 
    | 'bar' 
    | 'baz' 
    | 'squid';

text 
    : ~('<' | '>')+
    ;

ID
    : [0-9]+
    ;

这对吗?

【问题讨论】:

  • 问题表述得很好,但不太适合 SO,因为它主要是基于意见的。
  • 好点。我将修改问题以具体询问如何在antlr中实现。
  • 您的词法分析器可能只有两条规则:一个 TAG 和一个 CHAR,然后语法就是一个 (TAG | CHAR) *

标签: c# regex parsing antlr4 text-parsing


【解决方案1】:

一般而言,所识别的问题通常被描述为孤岛语法问题 - 否则单个文档的部分由两个或多个不同的,通常相互模棱两可的规范描述。

ANTLR 4 通过使用modes 直接支持岛语法。请注意,模式仅在拆分词法分析器/解析器语法中可用。

解析器

parser grammar TagsParser ;

options {
    tokenVocab = TagsLexer ;
}

parse   : ( tag | text )* EOF ;
tag     : LANGLE fixedlist GRIDLET ID RANGLE ;
text    : . ;
fixedlist
    : FOO
    | BAR
    | BAZ
    | SQUID
    ;

词法分析器

lexer grammar TagsLexer ;

LANGLE  : '<' -> pushMode(tag) ;
TEXT    : . ;

mode tag ;
    RANGLE  : '>' -> popMode ;

    FOO     : 'foo' ;
    BAR     : 'bar' ;
    BAZ     : 'baz' ;
    SQUID   : 'squid' ;
    GRIDLET : '#' ;
    ID      : [0-9]+ ;

    NONTAG  : . -> popMode ;

解析器中的text 规则将匹配之前没有被上面的解析器规则使用的所有标记。这将包括所有 TEXT 标记以及恰好与标记模式规则匹配但不是标记的有效一部分的任何文本。

【讨论】:

  • TEXT 定义的目的是什么?它似乎没有在词法分析器语法中使用,或者textTEXT 之间是否存在隐含关系我错过了?
  • 文档开头或NONTAG 之后和LANGLE 之前的任何内容都是TEXT
  • 是的,我知道从解析器的( tag | text )* 行,text 匹配任何不是标签的字符。我只是不明白词法分析器中的TEXT 行。换句话说,解析器text : . ; 和词法分析器TEXT : . ; 行之间有什么区别,以及TEXT 何时实际使用(它已定义,但对我来说似乎未使用)?
  • 在解析器启动之前,词法分析器运行完成,消耗字符并发出标记。解析器在匹配规则的过程中消耗令牌。因此,TEXT : . ; 为每个匹配的字符生成一个令牌,text: . ; 匹配 text 规则,用于非常之前未使用的令牌。
  • 为什么a &gt; b 应该被视为无效文本?最好在一个单独的问题中提出一个完整的解释,说明什么是有效文本、示例输入以及您试图表达有效性要求的语法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-10
  • 2010-12-09
  • 2021-02-26
相关资源
最近更新 更多