Tcl regsub:用其他字符'}'替换未转义的“

Tcl regsub: replace a unescapped " with other character '}'

我试图使用 regsub 将所有未转义的 " 替换为 }。我得到以下信息。

% set st {hello "hi this athat afsd\" adsfasdf"}
% regsub -all {(?!\)\"} $st \}
hello }hi this athat afsd\} adsfasdf}

我想要的是 hello }hi this athat afsd\" adsfasdf}。请让我知道哪里出了问题。

你必须使用反向引用来实现相同的目的。

set st {hello "hi this athat afsd\" adsfasdf"}
puts $st
regsub -all {([^\])"} $st \1\} result
puts $result

输出:

hello "hi this athat afsd\" adsfasdf"
hello }hi this athat afsd\" adsfasdf}

文档:regsub man page & regexp_how_to

好吧,您正在使用的正则表达式并没有真正按照您的想法行事。从字面上看,它会尝试匹配 (?!\)"(带有负前瞻的双引号,前面包含一个反斜杠)。

让我们尝试详细了解事情的进展情况。我将在比赛前后分别取一个样本,并添加空格以使其更清晰。

a f s d \ " 
       ^

此处引擎向前看并看到一个反斜杠。由于负前瞻,匹配失败,因此继续使用字符串。

a f s d \ "
         ^

既然它通过了反斜杠,它会再次尝试进行匹配。前面没有反斜杠,所以负前瞻不会停止匹配。接下来,正则表达式尝试匹配成功的双引号。

通常,您需要lookbehind 来执行您打算做的事情,因为前瞻无法检查它已经通过的内容,而否定的lookbehind 可以做到这一点。不幸的是,tcl 不支持 lookbehinds(并且实际上不需要一个,因为已经有更有效的解决方法)所以最好也匹配转义引号,但单独处理它们(这里通过将它们完全放回去是)。

例如,您可以使用

 regsub -all {(\")|"} $st {} result

如果您重新考虑使用 regsub,使用 string map 命令可以更简单地进行此转换:

string map {\\" \\" \" \}} $st

它的要点是"replace \" with \" (i.e. keep it unchanged) but replace " with }"。它看起来很尴尬,因为(在这种情况下)每个字符都需要转义,所以 \" 变成 \\" 等等,但它真的非常简单。

文档:string