我要解析:
([(A touch B) over C] touch {D touch E}) is good
。
使用:
( P1 touch P2) is good
。
我想用正则表达式替换P1
和P2
来获取
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
所以我们应该使用.*
答案 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。