Tcl regexp表达式用于捕获括号外的单词

时间:2017-12-06 01:46:42

标签: regex tcl

我要解析: ([(A touch B) over C] touch {D touch E}) is good

使用: ( P1 touch P2) is good

我想用正则表达式替换P1P2来获取 P1 = [(A touch B) over C] P2 = {D touch E} 我的第一个想法是: ( (.*) touch (.*)) is good

但我错了匹配: P1 = [(A touch B) over C] touch {D P2 = E}

我想打破括号外的"touch"。 注意:A, B, C... are examples所以我们应该使用.*

2 个答案:

答案 0 :(得分:2)

正则表达式匹配不能处理这种任意表达式,但大多数解析器都可以。在Tcl的Tcllib标准库中,pt(解析器工具)模块中有一个PEG(解析表达式语法)解析器生成器。下面定义了一个语法(作为一个简单的字符串),可以解析您的示例文本,以及同一类括号嵌套的文本:

set grammar {
PEG Reader (Datum)
      Datum       <- '(' <space>* P1 'touch' <space>* P2 ') is good' ;
      Word        <- <alpha>+ ;
      BrackExpr   <- '[' Expression+ ']' ;
      BraceExpr   <- '{' Expression+ '}' ;
      ParenExpr   <- '(' Expression+ ')' ;
void: Expression  <- (Word / BrackExpr / BraceExpr / ParenExpr) <space>* ;
      P1          <- Expression ;
      P2          <- Expression ;
END;
}

要使用此功能,您需要执行一些步骤。首先将生成的解析器保存在文件中:

package require fileutil
package require pt::pgen

fileutil::writeFile ./reader.tcl [pt::pgen peg $grammar oo -class Reader]

此代码创建一个文件reader.tcl,该文件包含执行语法中指定的解析的TclOO类。您source了该文件以使该课程可用:

source reader.tcl

如果要从控制台多次执行此操作,则需要在两者之间销毁该类:

catch {Reader destroy} ; source reader.tcl

然后你创建一个类的实例并将其投入使用:

Reader create reader
set str {([(A touch B) over C] touch {D touch E}) is good}
reader parset $str

(如果您使用parse方法,则可以解析开放频道。)

解析的结果是AST(抽象语法树)

Datum 0 47 {P1 1 21} {P2 28 38}

如您所见,它在字符串索引1到21处找到P1的文本,在28到38处找到P2的文本(请注意捕获尾随空格)。您可以使用string range $str 1 21获取P1的文本,或将其自动化:

proc Datum {from to args} {foreach arg $args {puts [uplevel 1 $arg]}}
proc P1 {from to} {string range $::str $from $to}
proc P2 {from to} {string range $::str $from $to}

% Datum 0 47 {P1 1 21} {P2 28 38}
[(A touch B) over C]
{D touch E}

如果您使用此功能,则可能需要尝试非终结Word的定义。目前它只允许使用字母字符。像

这样的定义
      Word        <- (<alnum> / [;:/&!?*+.\'\"#])+ ;

也允许数字和一些标点符号。包含更多内容的字符集,但<punct><graph><print>都包含括号字符。即使Expression的定义发生了变化

void: Expression  <- (BrackExpr / BraceExpr / ParenExpr / Word) <space>* ;

以便Word位于最后,这使得解析器在遇到开括号时选择其中一个*Expr非终结符,结束括号仍将由Word使用,而不是由li使用正确的非终结表达。 AFAICT这是PEG解析器的限制,它不会回溯。可以修改语法来处理这个问题,但很快就会变得太复杂。

文档: pt (package)

答案 1 :(得分:0)

您可以使用与[...]之前的{...}touch子字符串匹配的正则表达式模式,然后使用相同的模式。

看起来像

\((\[[^][]*]|{[^{}]*}) touch (\[[^][]*]|{[^{}]*})\) is good

请参阅regex demo at regex101(\[[^][]*]|{[^{}]*})构造是一个捕获组,它匹配除[[以外的] + 0+个字符,然后匹配]或{{1除了{{以及}之外,还有0 +个字符。

一句话:没有办法将嵌套平衡括号中的子串与Tcl正则表达式匹配。

使用Tcl代码时,您可以动态构建正则表达式:

}

输出:set txt {([(A touch B) over C] touch {D touch E}) is good} set squares {\[[^][]*\]} set braces {\{[^{}]*\}} set cap "($squares|$braces)" set rx "\\($cap touch $cap\\) is good"; lassign [lrange [regexp -all -inline $rx $txt] 1 end] P1 P2 puts "$P1 ::: $P2"

请参阅Tcl online demo