正则表达式,是一个描述一组字符串的模式。它主要用来文本搜索和字符串处理,一个正则表达式匹配单个字符或一个字符串,或者字符串的一部分。
在计算机科学中,正则表达式是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。在很多文本编辑器或其他工具里,正则表达式通常被用来检索和替换那些符合某个模式的文本内容。许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen。这些是正则表达式的定义。 由于起源于unix系统,因此很多语法规则一样的。但是随着逐渐发展,后来扩展出以下几个类型。
只有掌握了正则表达式,才能全面地掌握 Linux 下的常用文本工具(例如:grep、egrep、GUN sed、 awk 等) 的用法。
一、正则表达式分类:
1、基本的正则表达式(Basic Regular Expression 又叫 Basic RegEx? 简称 BREs)
2、扩展的正则表达式(Extended Regular Expression 又叫 Extended RegEx 简称 EREs)
3、Perl 的正则表达式(Perl Regular Expression 又叫 Perl RegEx 简称 PREs)
二、Linux 中常用文本工具与正则表达式的关系?
掌握 Linux 下几种常用文本工具的特点,对于我们更好的使用正则表达式是很有帮助的。
1)grep 支持:BREs、EREs、PREs 正则表达式
grep 指令后不跟任何参数,则表示要使用 ”BREs“?
grep 指令后跟 ”-E" 参数,则表示要使用 “EREs“
grep 指令后跟 “-P" 参数,则表示要使用 “PREs"
2)egrep 支持:EREs、PREs 正则表达式
egrep 指令后不跟任何参数,则表示要使用 “EREs”
egrep 指令后跟 “-P" 参数,则表示要使用 “PREs"
3)grep 与 egrep 正则匹配文件,处理文件方法
a. grep 与 egrep 的处理对象:文本文件
b. grep 与 egrep 的处理过程:查找文本文件中是否含要查找的 “关键字”(关键字可以是正则表达式) ,如果含有要查找的 ”关健字“,那么默认返回该文本文件中包含该”关健字“的该行的内容,并在标准输出中显示出来,除非使用了“>" 重定向符号,
c. grep 与 egrep 在处理文本文件时,是按行处理的。
1)sed 文本工具支持:BREs、EREs
sed 指令默认是使用"BREs"
sed 命令参数 “-r ” ,则表示要使用“EREs"
2)sed 功能与作用
a. sed 处理的对象:文本文件
b. sed 处理操作:对文本文件的内容进行 --- 查找、替换、删除、增加等操作
c. sed 在处理文本文件的时候,也是按行处理的
1)awk 文本工具支持:EREs
awk 指令默认是使用 “EREs"
2)awk 文本工具处理文本的特点
a. awk 处理的对象:文本文件
b. awk 处理操作:主要是对列进行操作
三、常见几种类型的正则表达式比较
下面的表格主要列出了几种类型的正则中常用的元字符和POSIX字符类,
字符 | 说明 | Basic RegEx | Extended RegEx | python RegEx | Perl regEx | 转义 | | \ | \ | \ | \ | ^ | 匹配行首,例如'^dog'匹配以字符串dog开头的行(注意:awk 指令中,'^'则是匹配字符串的开始) | ^ | ^ | ^ | ^ | $ | 匹配行尾,例如:'^、dog$'匹配以字符串 dog 为结尾的行(注意:awk 指令中,'$'则是匹配字符串的结尾) | $ | $ | $ | $ | ^$ | 匹配空行 | ^$ | ^$ | ^$ | ^$ | ^string$ | 匹配行,例如:'^dog$'匹配只含一个字符串 dog 的行 | ^string$ | ^string$ | ^string$ | ^string$ | \< | 匹配单词,例如:'\<frog' (等价于'\bfrog'),匹配以 frog 开头的单词 | \< | \< | 不支持 | 不支持(但可以使用\b来匹配单词,例如:'\bfrog') | \> | 匹配单词,例如:'frog\>'(等价于'frog\b '),匹配以 frog 结尾的单词 | \> | \> | 不支持 | 不支持(但可以使用\b来匹配单词,例如:'frog\b') | \<x\> | 匹配一个单词或者一个特定字符,例如:'\<frog\>'(等价于'\bfrog\b')、'\<G\>' | \<x\> | \<x\> | 不支持 | 不支持(但可以使用\b来匹配单词,例如:'\bfrog\b' | () | 匹配表达式,例如:不支持'(frog)' | 不支持(但可以使用,如:dogdog | () | () | () | | 匹配表达式,例如:不支持'(frog)' | | 不支持(同()) | 不支持(同()) | 不支持(同()) | ? | 匹配前面的子表达式 0 次或 1 次(等价于{0,1}),例如:where(is)?能匹配"where" 以及"whereis" | 不支持(同\?) | ? | ? | ? | \? | 匹配前面的子表达式 0 次或 1 次(等价于'\{0,1\}'),例如:'whereisis\? '能匹配 "where"以及"whereis" | \? | 不支持(同?) | 不支持(同?) | 不支持(同?) | ? | 当该字符紧跟在任何一个其他限制符(*, +, ?, {n},{n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个"o",而 'o+' 将匹配所有 'o' | 不支持 | 不支持 | 不支持 | 不支持 | . | 匹配除换行符('\n')之外的任意单个字符(注意:awk 指令中的句点能匹配换行符) | . | .(如果要匹配包括“\n”在内的任何一个字符,请使用:'(^$)|(.) | . | .(如果要匹配包括“\n”在内的任何一个字符,请使用:' [.\n] ' | * | 匹配前面的子表达式 0 次或多次(等价于{0, }),例如:zo* 能匹配 "z"以及 "zoo" | * | * | * | * | \+ | 匹配前面的子表达式 1 次或多次(等价于'\{1, \}'),例如:'whereisis\+ '能匹配 "whereis"以及"whereisis" | \+ | 不支持(同+) | 不支持(同+) | 不支持(同+) | + | 匹配前面的子表达式 1 次或多次(等价于{1, }),例如:zo+能匹配 "zo"以及 "zoo",但不能匹配 "z" | 不支持(同\+) | + | + | + | {n} | n 必须是一个 0 或者正整数,匹配子表达式 n 次,例如:zo{2}能匹配 | 不支持(同\{n\}) | {n} | {n} | {n} | {n,} | "zooz",但不能匹配 "Bob"n 必须是一个 0 或者正整数,匹配子表达式大于等于 n次,例如:go{2,} | 不支持(同\{n,\}) | {n,} | {n,} | {n,} | {n,m} | 能匹配 "good",但不能匹配 godm 和 n 均为非负整数,其中 n <= m,最少匹配 n 次且最多匹配 m 次 ,例如:o{1,3}将配"fooooood" 中的前三个 o(请注意在逗号和两个数之间不能有空格) | 不支持(同\{n,m\}) | {n,m} | {n,m} | {n,m} | x|y | 匹配 x 或 y,例如: 不支持'z|(food)' 能匹配 "z" 或"food";'(z|f)ood' 则匹配"zood" 或 "food" | 不支持(同x\|y) | x|y | x|y | x|y | [0-9] | 匹配从 0 到 9 中的任意一个数字字符(注意:要写成递增) | [0-9] | [0-9] | [0-9] | [0-9] | [xyz] | 字符集合,匹配所包含的任意一个字符,例如:'[abc]'可以匹配"lay" 中的 'a'(注意:如果元字符,例如:. *等,它们被放在[ ]中,那么它们将变成一个普通字符) | [xyz] | [xyz] | [xyz] | [xyz] | [^xyz] | 负值字符集合,匹配未包含的任意一个字符(注意:不包括换行符),例如:'[^abc]' 可以匹配 "Lay" 中的'L'(注意:[^xyz]在awk 指令中则是匹配未包含的任意一个字符+换行符) | [^xyz] | [^xyz] | [^xyz] | [^xyz] | [A-Za-z] | 匹配大写字母或者小写字母中的任意一个字符(注意:要写成递增) | [A-Za-z] | [A-Za-z] | [A-Za-z] | [A-Za-z] | [^A-Za-z] | 匹配除了大写与小写字母之外的任意一个字符(注意:写成递增) | [^A-Za-z] | [^A-Za-z] | [^A-Za-z] | [^A-Za-z] | \d | 匹配从 0 到 9 中的任意一个数字字符(等价于 [0-9]) | 不支持 | 不支持 | \d | \d | \D | 匹配非数字字符(等价于 [^0-9]) | 不支持 | 不支持 | \D | \D | \S | 匹配任何非空白字符(等价于[^\f\n\r\t\v]) | 不支持 | 不支持 | \S | \S | \s | 匹配任何空白字符,包括空格、制表符、换页符等等(等价于[ \f\n\r\t\v]) | 不支持 | 不支持 | \s | \s | \W | 匹配任何非单词字符 (等价于[^A-Za-z0-9_]) | \W | \W | \W | \W | \w | 匹配包括下划线的任何单词字符(等价于[A-Za-z0-9_]) | \w | \w | \w | \w | \B | 匹配非单词边界,例如:'er\B' 能匹配 "verb" 中的'er',但不能匹配"never" 中的'er' | \B | \B | \B | \B | \b | 匹配一个单词边界,也就是指单词和空格间的位置,例如: 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的'er' | \b | \b | \b | \b | \t | 匹配一个横向制表符(等价于 \x09和 \cI) | 不支持 | 不支持 | \t | \t | \v | 匹配一个垂直制表符(等价于 \x0b和 \cK) | 不支持 | 不支持 | \v | \v | \n | 匹配一个换行符(等价于 \x0a 和\cJ) | 不支持 | 不支持 | \n | \n | \f | 匹配一个换页符(等价于\x0c 和\cL) | 不支持 | 不支持 | \f | \f | \r | 匹配一个回车符(等价于 \x0d 和\cM) | 不支持 | 不支持 | \r | \r | \\ | 匹配转义字符本身"\" | \\ | \\ | \\ | \\ | \cx | 匹配由 x 指明的控制字符,例如:\cM匹配一个Control-M 或回车符,x 的值必须为A-Z 或 a-z 之一,否则,将 c 视为一个原义的 'c' 字符 | 不支持 | 不支持 | | \cx | \xn | 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长,例如:'\x41' 匹配 "A"。'\x041' 则等价于'\x04' & "1"。正则表达式中可以使用 ASCII 编码 | 不支持 | 不支持 | | \xn | \num | 匹配 num,其中 num是一个正整数。表示对所获取的匹配的引用 | 不支持 | \num | \num | | [:alnum:] | 匹配任何一个字母或数字([A-Za-z0-9]),例如:'[[:alnum:]] ' | [:alnum:] | [:alnum:] | [:alnum:] | [:alnum:] | [:alpha:] | 匹配任何一个字母([A-Za-z]), 例如:' [[:alpha:]] ' | [:alpha:] | [:alpha:] | [:alpha:] | [:alpha:] | [:digit:] | 匹配任何一个数字([0-9]),例如:'[[:digit:]] ' | [:digit:] | [:digit:] | [:digit:] | [:digit:] | [:lower:] | 匹配任何一个小写字母([a-z]), 例如:' [[:lower:]] ' | [:lower:] | [:lower:] | [:lower:] | [:lower:] | [:upper:] | 匹配任何一个大写字母([A-Z]) | [:upper:] | [:upper:] | [:upper:] | [:upper:] | [:space:] | 任何一个空白字符: 支持制表符、空格,例如:' [[:space:]] ' | [:space:] | [:space:] | [:space:] | [:space:] | [:blank:] | 空格和制表符(横向和纵向),例如:'[[:blank:]]'ó'[\s\t\v]' | [:blank:] | [:blank:] | [:blank:] | [:blank:] | [:graph:] | 任何一个可以看得见的且可以打印的字符(注意:不包括空格和换行符等),例如:'[[:graph:]] ' | [:graph:] | [:graph:] | [:graph:] | [:graph:] | [:print:] | 任何一个可以打印的字符(注意:不包括:[:cntrl:]、字符串结束符'\0'、EOF 文件结束符(-1), 但包括空格符号),例如:'[[:print:]] ' | [:print:] | [:print:] | [:print:] | [:print:] | [:cntrl:] | 任何一个控制字符(ASCII 字符集中的前 32 个字符,即:用十进制表示为从 0 到31,例如:换行符、制表符等等),例如:' [[:cntrl:]]' | [:cntrl:] | [:cntrl:] | [:cntrl:] | [:cntrl:] | [:punct:] | 任何一个标点符号(不包括:[:alnum:]、[:cntrl:]、[:space:]这些字符集) | [:punct:] | [:punct:] | [:punct:] | [:punct:] | [:xdigit:] | 任何一个十六进制数(即:0-9,a-f,A-F) | [:xdigit:] | [:xdigit:] | [:xdigit:] | [:xdigit:] |
四、特别地
注意: 当使用 BERs(基本正则表达式)时,必须在下列这些符号前加上转义字符('\'),屏蔽掉它们的 speical meaning? “?,+,|,{,},(,)” 这些字符,需要加入转义符号”\”。
注意:修饰符用在正则表达式结尾,例如:/dog/i,其中 “ i “ 就是修饰符,它代表的含义就是:匹配时不区分大小写,那么修饰符有哪些呢?常见的修饰符如下:
g?? 全局匹配(即:一行上的每个出现,而不只是一行上的第一个出现) s??? 把整个匹配串当作一行处理 m??? 多行匹配 i??? 忽略大小写 x??? 允许注释和空格的出现 U??? 非贪婪匹配
从Bash 3.0版本开始,Bash有了内部的正则表达式比较操作符,使用“=~”表示。大部分使用grep或sed命令的正则表达式编写脚本的方法,现在可以由带有“=~”操作符的Bash表达式处理,并且Bash表达式可能使你的脚本更容易阅读和维护。
if [[ $digit =~ [0-9] ]]; then
echo "$digit is a digit."
else
echo 'Oops'
fi
五、PCRE函数库
Nginx使用的正则表达式,是PCRE类的。
PCRE(Perl Compatible Regular Expressions)是一个用C语言编写的正则表达式函数库。
PCRE官网:http://www.pcre.org/
如何测试nginx正则:A Regular Expression Tester for NGINX and NGINX Plus - NGINX
Github项目:https://github.com/nginxinc/NGINX-Demos/tree/master/nginx-regex-tester
使用正则现成的Docker镜像:Docker Hub
参考:
PCRE正则表达式库,perl语言的灵魂
PCRE 简介_Cynhard的专栏-CSDN博客_pcre
PHP: PCRE - Manual
PCRE的安装及使用 - LiuYanYGZ - 博客园
PCRE的安装及使用 - LiuYanYGZ - 博客园
PCRE 库是一组函数,它使用与 Perl 5 相同的语法和语义实现正则表达式模式匹配。PCRE有自己的本地API,以及一组对应POSIX正则表达式API的封装函数。PCRE库是免费的,甚至可以用来构建专有软件。
PCRE最初是为Exim MTA编写的,但现在被许多高知名度的开源项目使用,包括Apache、PHP、KDE、Postfix和Nmap。PCRE也被一些著名的商业产品所采用,比如Apple Safari。其他一些使用PCRE的有趣项目包括Chicken、Onyx、Hypermail、Leafnode、Askemos、Wenlin和8th。
可以通过匿名 FTP 或 HTTPS 从其官方主页下载 PCRE 和 PCRE2 库的当前版本:
还可以通过 HTTPS 从?SourceForge?的非官方镜像?下载:
可以通过 Subversion 检出 PCRE2 源代码(或在线?浏览PCRE2源代码):
svn co svn://vcs.pcre.org/pcre2/code/trunk pcre
或者,要通过 Subversion 检出较早的 PCRE 源代码(或在线?浏览较早的PCRE源代码):
svn co svn://vcs.pcre.org/pcre/code/trunk pcre
六、正则引擎分类
正则引擎主要可以分为两大类:一种是DFA,一种是NFA。这两种引擎都有了很久的历史(至今二十多年),当中也由这两种引擎产生了很多变体!于是POSIX的出台产生规范了不必要变体的继续产生。这样一来,目前的主流正则引擎又分为3类:一、DFA,二、传统型NFA,三、POSIX NFA。
DFA 引擎在线性时状态下执行,因为它们不要求回溯(并因此它们永远不测试相同的字符两次)。DFA 引擎还可以确保匹配最长的可能的字符串。但是,因为 DFA 引擎只包含有限的状态,所以它不能匹配具有反向引用的模式;并且因为它不构造显示扩展,所以它不可以捕获子表达式。
传统的 NFA 引擎运行所谓的“贪婪的”匹配回溯算法,以指定顺序测试正则表达式的所有可能的扩展并接受第一个匹配项。因为传统的 NFA 构造正则表达式的特定扩展以获得成功的匹配,所以它可以捕获子表达式匹配和匹配的反向引用。但是,因为传统的 NFA 回溯,所以它可以访问完全相同的状态多次(如果通过不同的路径到达该状态)。因此,在最坏情况下,它的执行速度可能非常慢。因为传统的 NFA 接受它找到的第一个匹配,所以它还可能会导致其他(可能更长)匹配未被发现。
POSIX NFA 引擎与传统的 NFA 引擎类似,不同的一点在于:在它们可以确保已找到了可能的最长的匹配之前,它们将继续回溯。因此,POSIX NFA 引擎的速度慢于传统的 NFA 引擎;并且在使用 POSIX NFA 时,您恐怕不会愿意在更改回溯搜索的顺序的情况下来支持较短的匹配搜索,而非较长的匹配搜索。
目前使用DFA引擎的程序主要有:awk,egrep,flex,lex,MySQL,Procmail等; 使用传统型NFA引擎的程序主要有:GNU Emacs,Java,ergp,less,more,.NET语言,PCRE library,Perl,PHP,Python,Ruby,sed,vi; 使用POSIX NFA引擎的程序主要有:mawk,Mortice Kern Systems’ utilities,GNU Emacs(使用时可以明确指定); 也有使用DFA/NFA混合的引擎:GNU awk,GNU grep/egrep,Tcl。
举例简单说明NFA与DFA工作的区别: 比如有字符串this is yansen’s blog,正则表达式为?/ya(msen|nsen|nsem)/?(不要在乎表达式怎么样,这里只是为了说明引擎间的工作区别)。 NFA工作方式如下,先在字符串中查找?y?然后匹配其后是否为?a?,如果是?a?则继续,查找其后是否为?m?如果不是则匹配其后是否为?n?(此时淘汰msen选择支)。然后继续看其后是否依次为?s,e?,接着测试是否为?n?,是?n?则匹配成功,不是则测试是否为?m?。为什么是 m ?因为 NFA 工作方式是以正则表达式为标准,反复测试字符串,这样同样一个字符串有可能被反复测试了很多次! 而DFA则不是如此,DFA会从?this?中?t?开始依次查找?y,定位到?y?,已知其后为?a?,则查看表达式是否有?a?,此处正好有?a?。然后字符串?a?后为?n?,DFA依次测试表达式,此时?msen?不符合要求淘汰。nsen?和?nsem?符合要求,然后DFA依次检查字符串,检测到sen?中的?n?时只有?nsen?分支符合,则匹配成功!
由此可以看出来,两种引擎的工作方式完全不同,一个(NFA)以表达式为主导,一个(DFA)以文本为主导!一般而论,DFA引擎则搜索更快一些!但是NFA以表达式为主导,反而更容易操纵,因此一般程序员更偏爱NFA引擎!
两种引擎各有所长,而真正的引用则取决与你的需要以及所使用的语言!
参考:
linux shell 正则表达式(BREs,EREs,PREs)差异比较_weixin_30634661的博客-CSDN博客
转载(正则表达式的分类) - 规格严格-功夫到家 - 博客园
正则表达式的分类 - 王子建 - 博客园
你是如何学会正则表达式的? - 知乎
正则表达式在线测试 | 菜鸟工具
正则表达式测试工具 - 在线工具
Have Fun
|