“缺失”的preg函数

04-13Ctrl+D 收藏本站

关灯 直达底部

\"Missing\"Preg Functions

PHP内建的preg函数已经提供了繁多的功能,但是有时候我仍然发现它们不够用。一个例子是我自己开发的preg_match(☞454)。

我发现,另一类需要提供自己的支持函数的情形是,正则表达式不是在程序内部通过pattern参数字符串提供的,而是来自程序外部(例如,从文件读入,或者是在Web表单中提交)。下一节我们将会看到,把纯粹的正则表达式字符串转换为适合 patern 参数使用的形式也很复杂。

同样,在使用这些正则表达式之前,通常都必须验证他们的语法正确性。我同样会讲解这些问题。

与本书中的所有程序代码一样,下一页的函数也可以从我的网站下载:http://regex.info/。

preg_regex_to_pattern

如果正则表达式包含在字符串中(可能是从配置文件读入,或者通过 Web 表单提交),在preg函数中使用时,首先必须在两端加上分隔符,才能生成一个preg函数能用的pattern参数。

问题所在

许多时候,把正则表达式转换为pattern参数只不过是在两端加上斜线而已。这样,正则表达式字符串‘[a-z]+’ 就成了‘/[a-z]+/’,可以用作preg函数的pattern参数的字符串。

如果正则表达式中包含用作分隔符的字符,情况就很复杂。例如,正则表达式是‘^http://([^/:]+)’,仅仅在两端添加反斜线得到‘/^http://([^/:]+)/’,用作pattern时,结果会是“Unknown modifier/”。

第 448 页已经介绍过,这个错误信息是因为字符串中的前两个斜线字符被当作分隔符,之后的部分(在这里就是第3个斜线之后的部分)被当作修饰符序列了。

解决之道

有两种办法能解决内嵌分隔符的问题。之一是选择正则表达式中没有出现的分隔符,如果需要手工构造pattern-modifier字符串,那么这当然是推荐的办法了。所以我在第444、449和450页(还有许多)的例子中使用{…}作为分隔符。

要选出正则表达式中没有出现的分隔符可能并不容易(甚至不可能),因为字符串中可能包含所有分隔符,或者你不能预先知道需要处理的文本。在实际应用正则表达式时这需要特别关注,所以最简单的办法是使用第二种:选择一个分隔符,然后对正则表达式字符串中出现的此分隔符进行转义。

问题可能比初看起来要困难许多,因为你必须关注某些重要的细节。例如,在目标字符串末尾的转义必须进行特殊处理,保证它不会转义紧跟在后面的分隔符。

下面的函数接收正则表达式字符串,以及可能出现的 pattern-modifier 字符串,返回一个可以用于preg函数的pattern字符串。代码中难看的反斜线(正则表达式和PHP子串转义)或许是你见过的最复杂的表示;这段代码并不容易读懂(如果你希望补习PHP单引号字符串的语意,请参考第444页)。

每次需要的时候重新写这个函数太麻烦,于是我将它封装到一个函数中(我希望它会成为preg套件中的内建函数)。

有兴趣的读者不妨想想函数尾部preg_replace_callback使用的正则表达式:它如何工作,以及回调函数如何遍历整个pattern字符串,转义每个未转义的斜线,而不修改已转义的斜线。

对未知的Pattern参数进行语法检查

Syntax-Checking an Unknown Pattern Argument

在正则表达式两端添加分隔符之后,我们确信它适于用作preg函数的pattern参数了,但是原来的正则表达式还没有经过语法正确性检验。

举例来说,如果原始的正则表达式是‘*.txt’,因为某些人希望使用文件群组功能(☞4)而不是正则表达式,那么preg_regex_to_pattern返回的就是/*.txt/。这个正则表达式当然不合法,所以程序会发出警报(如果启用了警报功能):

Compilation failed:nothing to repeat at offset 0

PHP没有内建检测pattern参数及其正则表达式的语意是否合法的函数,不过我为读者提供了一个。

preg_pattern_error会对pattern 参数进行简单测试,试图使用此正则表达式——在函数当中有一行调用preg_match。函数的其他部分使用关注PHP的管理功能处理preg_match可能显示的错误信息。

对未知正则表达式进行语法检查

Syntax-Checking an Unknown Regex

这个函数使用刚刚开发的功能来检查一个纯正则表达式(没有分隔符也没有模式修饰符)的语法。如果语法不正确,会返回对应的错误信息,如果语法正确,返回false。