来自《粉碎杂志》的原创英语。由愚蠢的作品翻译。请指出来源。正则表达式功能强大,可以用来在一大串字符中找到所需的信息。它用常规的人物结构表达来行动。不幸的是,简单的正则表达式对于一些高级应用程序来说是不够的。如果过滤的结构复杂,您可能需要使用高级正则表达式。本文向您介绍正则表达式的高级技术。我们筛选出八个常用的概念,并举例分析。每个例子都是简单的写作,满足了一些复杂的需求。如果你不知道规律性的基本概念,请先阅读本文,或本教程,或wiki词条。这里的常规语法适用于PHP,并且与Perl兼容。
所有可以多次限定的正则算子都是贪婪的。它们匹配尽可能多的目标字符串,这意味着匹配结果将尽可能长。不幸的是,这种做法并不总是我们想要的。因此,我们添加了“懒惰”限定词来解决这个问题。添加“?”在每个贪婪的操作符之后。让表达式只匹配尽可能短的长度。此外,修饰语“u”还可以钝化可以多次限定的操作符。理解贪婪和懒惰的区别是使用高级正则表达式的基础。00-1010运算符*与前面的表达式匹配零次或更多次。它是一个贪婪的算子。请看下面的例子:preg_match('/h1。*/h1/',' h1这是一个标题。/h1 h1这是另一个。/h1 ',$ matches);句号(。)可以表示除换行符以外的任何字符。上面的正则表达式匹配h1标签和标签中的所有内容。它使用句点(。)和星号(*),以匹配标记中的所有内容。匹配结果如下:h1这是一个标题。/h1 h1 h1这是另一个。/h1返回整个字符串。*操作员将连续匹配所有内容——,甚至包括中间的h1结束标记。因为是贪婪的,整串匹配符合利益最大化的原则。
单词边界是单词字符(包括字母、数字和下划线,当然也包括汉字)和非单词字符在字符串中的位置。它的特别之处在于它与真实的角色不匹配。它的长度为零。b匹配所有单词边界。遗憾的是,词的界限一般被忽略,大多数人并不关心它的实际意义。比如要匹配“导入”这个词:/导入/注意!正则表达式有时很调皮。下面的字符串也可以用上面的公式成功匹配:重要你可能认为只需要在导入前后加空格就可以匹配这个独立的单词:/import/如果出现这种情况怎么办:交易者投票赞成导入当单词import在字符串的开头或结尾时,修改后的表达式仍然不能使用。所以要考虑各种情况:/(import | import | import $)/我不慌,还没完。遇到标点符号怎么办?为了满足这一个字的匹配,你可能需要write://(^import(:| |;|,)?|导入(: |;|,)?|导入(。|?|!)?$)/i只匹配一个单词有点咄咄逼人。正因为如此,词的界限意义重大。为了满足上述要求,以及许多其他变化,有了字符边界,我们只需要编写代码:/ b端口b/上述所有情况都已解决。b的灵活性在于它是一个没有长度的匹配。它只匹配两个实际角色之间的假想位置。它检查两个相邻的字符是否是单个单词,另一个是否是非单个单词。如果情况匹配,则返回匹配。如果遇到单词的开头或结尾,b将把它当作非单词字符。既然我在导入时仍然被视为一个单词字符,导入是匹配的。请注意,与b相反,我们还有B,它匹配两个单词或两个非单词之间的位置。因此,如果您想在单词内部匹配“hi”,可以使用: bhi b“this”和“hight ”,这将返回一个匹配,但“hi那边”不会返回匹配。
最小分组是没有捕获的特殊正则表达式分组。通常用于提高正则表达式的性能,也用于消除特定的匹配。最小组可以使用(?模式),其中模式是匹配类型。/(?His|this)/当常规引擎匹配到最小的组时,会跳过组中标记的回溯位置。以“砸”字为例,在与上述正则表达式匹配时,正则引擎会先尝试在“砸”字中找到“他的”。显然,没有找到匹配。此时,最小组开始发挥作用:常规引擎将放弃所有回溯位置。也就是说,它不会试图从“砸”中找到“这个”。为什么要这样设置?因为“他的”没有返回匹配结果,当然,“这个”包含“他的”不能匹配!上面的例子不实用,所以我们用/t?他的?/也能达到效果。看下面的例子:/b(engineer | improve | end) b/如果用“engineering”进行匹配,常规引擎会先匹配“engineer”,但之后会遇到单词boundary, b,所以匹配不成功。然后,常规引擎会尝试在字符串中找到下一个匹配:雕刻。匹配到eng时,后一个没有再匹配,匹配失败。最后,尝试“结束”,结果也是失败。仔细观察就会发现,一旦工程师匹配失败,两者都到达字界,那么“刻”和“端”这两个字是不可能匹配成功的。这两个字比工程师短,正规的引擎不应该再做不必要的尝试。/b(?工程师|入侵|结束) b/以上替代编写方法可以节省常规引擎的匹配时间,提高代码的工作效率。
递归用于匹配嵌套结构,如括号嵌套、(this (that))和HTML标记嵌套divdiv/div/div。我们用(?r)来表示递归过程中的子模式。下面是匹配嵌套括号:/((?[^()] )|(?R))*)/最外面的括号“”(带反义符号)匹配嵌套结构的开头。然后是一个多选项运算符(* | *),它可以匹配除方括号外的所有字符”(?[()])”,或者通过子模式”(?r)”来再次匹配整个表达式。请注意,该运算符匹配尽可能多的嵌套。递归的另一个例子如下:/([w])。*?((?[^] )|((?R)))*/1/以上表达式综合使用了字符分组、贪婪算子、回溯和最小化分组来匹配嵌套标签。第一个括号分组([w])匹配下一个应用程序的标记名。如果您找到这个尖括号标签,请尝试找到标签内容的其余部分。下一个括号子表达式与上一个示例非常相似:要么匹配除尖括号之外的所有字符?[],或者递归匹配整个表达式(?r ).表达式末尾的/1表示封闭标签。
匹配结果中的具体内容有时可能需要一些特殊的修改。为了应用多种复杂的修改,正则表达式的回调将发挥作用。回调用于动态修改函数preg_replace_callback中的字符串。您可以为preg_replace_callback指定一个函数作为参数,该函数可以接收匹配的结果数组作为参数,修改数组并将其作为替换结果返回。例如,我们希望将字符串中的所有字母都转换为大写。不幸的是,PHP没有直接转换字母大小写的常规运算符。为了完成这个任务,您可以使用常规回调。首先,表达式应该匹配所有需要大写的字母:/bw/上面的公式使用了单词边界和字符类。光有这个公式是不够的。我们还需要一个回调函数:函数upper _ case($ matches){ return strtopper($ matches[0]);}函数upper_case接收匹配结果数组,并将整个匹配结果转换为大写。在本例中,$matches[0]代表需要大写的字母。然后,我们使用preg_replace_callback实现回调:preg _ replace _ callback ('/ b w/','大写',$ str);一个简单的回调是如此强大。
注释不用于匹配字符串,但它们是正则表达式中最重要的部分。随着常规书写越来越深、越来越复杂,推导出匹配的内容会越来越困难。在正则表达式中间添加注释是最大限度地减少未来混淆和混乱的最好方法。若要注释正则表达式,请使用(?#comment)格式。将“注释”替换为您的注释声明:/(?# Numbers) d/如果打算公开代码,注释正则表达式尤为重要。只有这样,别人才能更容易理解和修改你的代码。和其他场合的笔记一样,这也会为你重温之前写的程序提供便利。考虑使用“x”或“(?x)"修饰符来格式化注释。该修饰符允许常规引擎忽略表达式参数之间的空格。“有用”空格仍然可以用[]或s或(反义加空格)来匹配。/d #数字[]#空格 w #单词/x上面的代码与下面的公式有相同的功能:/ d(?#digit)[ ](?#空格)w(?#word)/请时刻注意代码的可读性。