文本匹配语言是理解 Proxomitron 的过滤如何实现的关键。它允许你匹配复杂的 HTML 标签组合,把部分被匹配的文本储存进变量,并在替换文本部分随意使用这些变量。
如果你熟悉 DOS 和 UNIX 的文件名通配符(包括* ? [...])或正则表达式,你会发现 Proxomitron 的匹配规则有很多熟悉的部分。事实上我原先是想创建一个几乎像通配符一样易于使用的匹配语言,并加入强大的正则表达式。我不知道我是否做到了,但无论如何我总算让它们都能正常工作!;-)
如果这些对你来说都是从未接触过的东西,请务必先看最基本的文本匹配入门教程。
很多元字符为了能更好地处理 HTML 代码都经过了特别设计。例如,由于大小写对于HTML来说并不重要,所有的元字符都无视大小写 - 省去你为大小写测试的时间。
Proxomitron 的元字符
这是一个 Proxomitron 的元字符完整列表及功能说明。
* | 星号匹配任意字符串。例如,"foo*bar" 匹配 "foobar", "fooma babar", 或是 "foo goat bat bison bar"。基本上星号的意思是 "向前搜索并完全匹配,直到搜索到星号后的字符"。 |
? | 问号匹配任意单个字符。例如 "?oat" 匹配 "boat" 或 "goat" 或 "<oat" |
[abc...] | 中括号匹配 '[' 和 ']' 中的任意字符。用横杠 "-" 可以使用范围匹配: "[A-Z]" 可以匹配从 "A" 到 "Z" 的任意字符;而 "[0-9]" 匹配任意数字。 如果以 "^" 开头,则匹配任意 不在 方括号内的字符 - "[^0-9abc]" 匹配任意既不是数字也不是 "a",或 "b",或 "c" 的字符。 |
[#n:n] | 整数区间匹配。可以用来方便地检查 HTML 标签中的数值大小。例如,"[#100:150]" 可以匹配任意大于等于100且小于等于150的整数。如果第二个数字 'n' 是 '*' ,则匹配至无穷大, "[#40:*]" 匹配任意大于等于40的整数。而 "[#0:40]" 可以匹配任意大于等于0小于等于40的整数。匹配特定数字时,可以省略第二个 'n',例如 "[#100]" 匹配数字100。记住,数字区间匹配忽略引号和起始的0 - 例如 tag="0100"。 早期的 Proxomitron 用 "-" 代替 ":" 来分隔数字,但这样很难匹配负数。 现在的 Proxomitron 两种方式都可以用,但是当匹配负数时(例如 [#-200:150])务必使用 ":"。 |
" " | 空格(译注:这是一个空格,不是一对引号,也不是一对引号包含一个空格) 总是 在匹配的同时 消耗 它能找到的任意数量个空格或制表符或换行符或回车符。不论有没有空格的情况下都可以使用。例如,
"<tag value>" 匹配 "<tag value>" 或 "<tag value>" 或 "<tagvalue>"。 |
\s | 反斜杠+s: 和空格一样,匹配并消耗它能找到的任意数量个空格或制表符(Tab),但 至少匹配一个空格。例如 "<tag\s>" 匹配 "<tag >" 或 "<tag >" ,但不匹配 "<tag>" |
\w | 反斜杠+w: 单词匹配。匹配任意数量的非空白字符。它基本上就是 "\s" 的反义,但它也有点类似 "*"。它们的区别在于,\w 遇到空格或 ">" 时会停止匹配,因此在匹配标签值或URL时很方便。(参见 心得与技巧) |
\t | 匹配一个制表符 |
\r | 匹配一个回车符 |
\n | 匹配一个换行符 |
\0-9 | 反斜杠+数字 0-9: 将匹配捕获进变量。这是 Proxomitron 的关键匹配规则之一。它的匹配效果相当于 "*" ,只是把匹配到的东西全部储存在变量里。 在随后的替换文本中可以引用这些变量从而引用原网页的代码。常见于用来改变标签的部分内容但保留其他内容。例如,只改变 <body ... > 标签的 "background" 属性,匹配替换文本如下...
Matching:<body \1 background="*" \2 > Replace:<body \1 background="mybackground.gif" \2 > 这样,body标签中除了被修改部分的所有其他内容都会被完整地插入替换文本中。 \0-9可以用于更多复杂的匹配。像 "(foo*bar)\1" 这样,\n 紧随 一对括号后面,表示括号里的所有内容都会被存放进变量里。这类似于正则表达式,但好处是可以从10个变量中自由选择希望存放的变量。 |
\# | 反斜杠+井号: 和\0至\9很类似,但区别在于,每次使用\#时都会把所匹配的内容加入替换堆栈内。也就是说,新的匹配内容会被放在前一次的匹配内容的后面一并保存。例如,如果 \# 先匹配了 "foo" 之后又匹配了 "bar",它会同时包含 "foo" 和 "bar" 两个值。在替换文本部分使用 "\@" 会输出\#所匹配到的所有内容,即 "foobar"。 |
| | 竖线代表 "OR" 函数。例如 "foo|bar" 可以匹配 "foo" 或 "bar"。 |
& | "&" 符号代表 "AND" 函数。例如,"*foo&*bar" 匹配 "foo bar" 或 "bar foo" 但不匹配 "foo foo"。注意这里的"*" - 一般都要在 "AND" 函数里写上。因为在同一个位置同一个时间,一个单词不可能 既是 "foo" 又是 "bar";)。 在标签属性的次序不确定时, "AND" 函数会很有用。如下代码...
<img src="picture" height=60 width=200> 也可以写成... <img width=200 height=60 src="picture"> 但无论哪种写法,它们都会被如下规则匹配... <img (*src="picture" & *height=60 & *width=200)*> |
&& | 两个 "&" 符号: "AND AND" 函数,和 "&" 效果几乎一样。但有一点重要的区别很有用 - 第二个 "&" 表示 精确 匹配第一部分。 没看懂?其实很简单。例如在如下的表达式中...
(<img * > && \1 ),"\1" 通常会和 "*" 的效果一样。如果用的是普通的 "AND" 函数,则会匹配从 "<img " 一直到终了的所有文本(超过 img tag 的结尾)。而 "AND AND" 函数则限制只匹配 <img ... > 标签里的内容。这样一来,\1 只捕获<img ...> 标签。你可以像范围匹配一样使用 "&&" 函数来限制匹配发生的范围,防止匹配过多。 |
( ... ) | 使用括号匹配多个分支条件。例如, "foo(bar|bear|goat)" 可以匹配 "foobar", "foobear" 或 "foogoat"。括号里也可以互相嵌套,例如 "foo(bar|(black|brown|puce) bear|goat)" 可以匹配 "foobar" "fooblackbear" "foobrownbear" 等等... 和 "[...]" 一样,如果 "(" 后面的第一个字符是 "^",该表达式则只会匹配 不 符合条件的文本。例如, "(^foo|bar)" 可以匹配所有既不是 "foo" 也不是 "bar" 的文本。 注意这种否定方式的表达式并不会消耗字符,仅仅检测字符。我记得在 Perl 里这叫 "负向先行断言"? |
+ | 加号表示连续重复匹配。例如, "a+" 匹配 "a", "aa", 或 "aaaa"。 为了实现更复杂的匹配,你可以在其他元字符或括号后面使用它。例如...
[abc]+ 匹配一串连续的包含字符 "a","b", 或 "c" 的字符串,像是 "ababccba" ([a-z]&[^n])+ 循环匹配一串连续的包含从 "a" 到 "z" 的任意字母,除了 "n" 的字符串 (foo)+ 匹配 "foo", "foofoo", "foofoofoo", 等等。 关于 + 有个重点:它是"盲"匹配。意思是,只要 "+" 前面的条件成立,它就会一直匹配下去,而不管 "+" 后面的条件是否成立! 例如 "(foo)+foobar" 永远无法匹配任何文本。为什么?因为前面的循环会吃掉所有的 "foo",于是就不会剩下可以匹配 "foobar" 的 "foo" 了!这种贪婪匹配的方式有时候是非常有用的,但如果你不希望这种情况的发生,可以用 "++"。 译注:"+" 也可以匹配空。例如 "abc+",可以匹配 "abc", "abcc", "abccc"... 和 "ab"。 |
++ | 双加号,和 "+" 差不多,也是循环匹配,但是它会在匹配的同时关注它后面的条件,或者说它可以 "看见"。它会循环匹配直到它后面的表达式匹配。这有点像普通正则表达式中的 "." 操作符。 |
{n,n} | "+" 和 "++" 后面都可以加上一对这样的大括号,用来控制循环匹配的最小匹配次数和最大匹配次数。例如,"[a]+{4,10}" 可以匹配一个包含4至10个 "A" 的字符串;"[b]+{20}" 可以匹配一个正好包含20个 "B" 的字符串;星号 "*" 则表示 "无限"次数 。例如,"[c]+{10,*}" 可以表示10个或10个以上的 "C"。(如果你熟悉正则表达式,请注意这和正则表达式有区别:大括号前面 一定 要有 "+" 或 "++" 。单独的大括号是无法正常工作的。) |
\ | 反斜杠可以用来 "编码" 任何有特殊意义的字符,从而把它们当作普通字符来对待。例如,匹配HTML文本中的括号时用"\(";匹配反斜杠本身时用 "\\"。 |
= | 等号比较特殊。它不仅仅匹配 "=" 本身,也会匹配它前后的任何空格类字符 - 这可以用来简单地匹配HTML的标签属性值。例如 foo="bar" 也匹配 foo= "bar" 或 foo = "bar" |
" | 双引号 - 它不仅匹配双引号,也匹配单引号(因为在HTML里两者皆可使用)。例如 " * " 可以匹配...
"oh happy mongoose" ...或... 'oh happy mengeese' 如果你仅仅想匹配双引号,使用反斜杠来编码 - \" |
' | 单引号可以智能匹配:它可以匹配之前双引号匹配过的开始引号的 结束引号,而不管它们中间是否有别的引号!很难懂?没关系。HTML和语文一样,经常会遇到引号套引号的情况,像下面的例子...
双引号套单引号: href=" javascript:window.open( ' bison.html ' ); " 或... 单引号套双引号: href=' javascript:window.open( " bison.html " ); ' 这两种都会被 href=( " * ' ) 匹配:先用双引号匹配了最开始的那个引号,然后用单引号匹配到开始引号的配对结束引号。但有一些限制:第一, 开始引号和结束引号都必须在同一个分支条件里 - 也就是说它们必须在同一个括弧里,例如.... " some text ' 可以正常匹配... ( " some text ' ) 也可以正常匹配... " ( some text | other text) ' 也可以正常匹配... 但 " ( some text ' ) 不能正常匹配 ( " | ) some text ( ' | )很遗憾,也不能正常匹配 另一个限制是开始引号和结束引号不能在同一个分支条件里进行嵌套。例如... " something " something else ' end of something '不能正常匹配 但是,你可以把它们放在 不同的 分支条件里进行嵌套,例如... " something ( " something else ' ) end of something ' 如果之前没有双引号进行匹配,那么单引号当然也无法匹配“结尾引号”,它只会匹配一个普通的单引号。不过为了安全起见,在只希望匹配单引号的情况下最好还是用 \' 来指定匹配。 |
特殊替换文本代码
除了以上的匹配规则,在替换文本里还可以另外使用一些特殊代码。
首先, "\0" 到 "\9" 可以用来插入在匹配表达式里储存的内容。对于用 "\#" 捕获的内容,既可以用 "\@" 引用之前储存的所有内容,也可以反复用 "\#" 来引用它每次储存的内容。(例如在匹配文本里使用了三次\#储存了三段不同的内容,使用 "\# \# \#" 可以把三段内容全部输出,每段内容之间用空格分隔。)
另一些可以在替换文本里使用的特殊代码...
\u | 返回当前网页的完整URL地址 |
\k | 中断当前连接 - 在HTTP headers过滤规则里可以用来禁止连接某些特定URL;在网页过滤规则里可以用来中止载入剩余的网页。 |
\h | 返回当前URL的主机名部分(译注:http://www.abc.com/abc/abc.htm的www.abc.com部分) |
\p | 返回当前URL的路径部分(译注:http://www.abc.com/abc/abc.htm的abc/abc.htm部分) |
\q | 返回当前URL的查询字符串部分 (URL中"?"后面的部分) |
\a | 返回当前URL的锚点部分 (URL中"#"后面的部分) |
\d | 返回以"file://"开头的程序根目录URL (用来替换图片或数据) |
\x | 返回URL命令前缀 (小心! You don't want to accidentally give it to a remote server). |
Note: \h \p \q \a 和 \u 也可以用在匹配表达式部分。尤其是 \h,用来检查网页源码里的URL是否和当前URL一致很有用
匹配命令(扩展功能)
除了上面提到的所有元字符,Proxomitron也有特殊的 匹配命令。这些命令加入了很多有用的函数,扩展了匹配规则。它们都以 "$" 加上一个大写名称开头,后面跟着一对括号 "(...)",括号里则是该命令的参数。例如 $LST(...) 命令,可以用来检查blocklist。例如...
<img * src="$LST(ImageURLCheck)" * >
可以用来检查img标签的src属性是否能在名为"ImageURLCheck"的blocklist里找到匹配。还有很多其他这样的命令可以让设计过滤列表变得更加容易。上面的例子命令用于匹配表达式部分,其他的有些可以用于匹配表达式部分,有些可以用于替换文本部分,有些两者皆可。点击这里可以获得更多资料。
这允许我更容易添加新的命令而不会造成冲突或是添加了不必要的元字符。结束了...?
差不多就是这些规则了。想通过查看更多例子了解如何使用它们的话,参见心得与技巧
