"替换堆栈" 是在 Proxomitron Naoko4 里引入的新功能。Proxomitron 支持十个匹配变量 \0 到 \9,一般来说是够用的。不过仍然不能完全满足需求。
例如,使用 "+" 或 "++" 表达式循环匹配时,如果你想要把搜索到的每项都存入不同的变量的话怎么办?以下面这个匹配 URL 路径的规则为例...
http://(*/)+*.html
(*/)+ 部分会匹配 URL 的每个部分,但怎么才能把这些捕获到的部分存入不同的变量?这就是替换堆栈出场的时候了。它像 "\0" 到 "\9" 一样,使用特殊的字符 "\#" 储存一个匹配值。但是每次调用 "\#" 时都会把匹配值储存进 "堆栈",最多可以容纳100个。这样,在之后的替换文本部分就可以根据先进先出的原则调出这些堆栈。应用到上面的匹配...
http://(\#/)+\#.html
然后在替换文本部分这样写...
"\# \# \# \# \# \# \#"
这样就可以把这样的 URL...
http://this/is/a/test/of/the/stack.html
转换为...
"this is a test of the stack.html"
每个 (...)+ 的循环都会把新匹配的值推送进堆栈,最后剩下来无法匹配的部分被第二个 "\#" 匹配。当然,和其他的位置变量一样,堆栈变量也可以在括号后使用以捕获括号内的表达式。例如...
http://(*/)\#+\#
会输出...
"this/ is/ a/ test/ of/ the/ stack.html"
在替换文本部分我们也可以使用另一个特殊字符 "\@"。它会一次性把堆栈里的全部内容按顺序输出(像是使用 \#\#\#\#\#\#\#\#...)。事实上,你多数时候都会使用这个。
一些典型用途...
你完全可以把 \# 看成是和 \1, \2, \3 一样,除了每次调用时,内容都不会被替换而是被推送进堆栈尾部。一些例子...
移除标签属性:
Bounds: | <Sometag\s*> |
Matching: | (\#(attr1|attr2|attr3)=$AV(*))+ \# |
Replace: | \@ |
- 第一个 "\#" 会捕获第一个需要移除的属性之前的所有文本(如果有的话)
- 然后下面的循环会匹配所有需要移除的属性和属性值。
- 最后的 \# 会循环匹配完后的剩下的文本;如果循环没有任何匹配,这个 \# 则会匹配整个标签。
只保留需要的属性 (移除其他任何东西):
Bounds: | <Sometag\s*> |
Matching: | (*((attr1|attr2|attr3)=$AV(*) )\#)+ * |
Replace: | <Sometag \@> |
这和刚才的例子用途相反,但语法上非常接近。它会去掉除了需要的属性和属性值以外的所有东西。这里我们只用一个 \# 就捕获了全部。
替换属性值:
Bounds: | <Sometag\s*> |
Matching: |
(\#((attr1=)\#foo$SET(\#=bar)| (attr2=)\#black$SET(\#=white)| (attr3=)\#one$SET(\#=zero)))+ \# |
Replace: | \@ |
这个规则有一点点复杂。注意它在普通匹配中使用了 $SET(\#=...) 命令。这可以用来做一些有趣的事情。
上面的例子首先寻找任意一个它可以匹配的属性值。找到后,它会把属性名推送进堆栈,并尝试匹配该属性的值。如果它成功匹配了属性值,就会用 $SET 把一个新值推送进堆栈。另外它捕获并保存其他不能匹配的东西。你甚至可以用一个过滤名单来代替属性值...
(\#$LST(AttributeReplace))+ \#
名单条目...
# # Sample attribute replacement list # (attr1=)\#foo$SET(\#=bar) (attr2=)\#black$SET(\#=white) (attr3=)\#one$SET(\#=zero)
或者...
# # Sample attribute replacement list two # attr1=foo $SET(\#=attr1=bar ) attr2=black $SET(\#=attr2=white ) attr3=one $SET(\#=attr3=zero )
来有选择性地同时替换属性和属性值(事实上因为名单可以被索引,这种方法会匹配更快,效果更好)。
