💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 正则表达式 [TOC] ## 运算顺序 1. `( )` 圆括号因为是内存处理所以最高 2. `* ? + { }` 重复匹配内容其次 3. `^ $ \b` 边界处理第三 4. `|` 条件处理第四 最后按照从左到右来匹配 ## 原子 原子是正则表达式中的最小的元素,包括英文、标点符号等。**代表只匹配一个** \d 匹配任意一个数字 [0-9] \D 与除了数字以外的任何一个字符匹配 [^0-9] \w 与任意一个英文字母,数字或下划线匹配 [a-z0-9A-Z_] \W 除了字母,数字或下划线外与任何一个字符匹配 [â-z0-9A-Z_] \s 与任意一个空白字符匹配 [\n\f\r\t\v] \f 换页字符; \n 换行字符; \r 回车字符; \t 制表符; \v 垂直制表符; \S 与除了空白符外任意一个字符匹配 [^\n\f\r\t\v] ## 元字符 代表着特殊意义的特殊字符 . 除换行符以外的任何一个字符 | 或的意思,匹配其中一项就代表匹配成功 ```javascript /^\d{15}|\d{18}$/ //匹配身份证号,旧版是15位数字,新版是18位数字 /\S+/ //匹配不包含空白符的字符串。 /<a[^>]+>/ //匹配用尖括号括起来的以a开头的字符串。 ``` ## 原子表 相当于或的一个集合,只匹配到原子表中的任一个就代表成功. [] 只匹配其中的一个原子 [0-9] 匹配0-9任何一个数字 [a-z] 匹配小写a-z任何一个字母 [A-Z] 匹配大写A-Z任何一个字母 [^]  只匹配"除了"其中字符的任意一个原子 ```javascript /[^x]/ //匹配除了x以外的任意字符 /[^aeiou]/ //匹配除了aeiou这几个字母以外的任意字符 ``` > 上面都是只匹配一个字符,如果要匹配多个字符,用原子分组; ## 原子分组 分组代表一个原子集合或者说一个大原子(每个括号必须要全部按顺序),并压入堆栈(内存)用于调用,组号是从左到右计数的调用时:在js中如果是字面量形式用\1,构造函数方式用\\1 这种方式我们叫做反向引用,如果不想让其到内存中可以用(?:exp)来匹配;另外,在php中\\0代表匹配到的全部内容;\\1,\\2..同上 ```javascript //构造函数方式: var reg = new RegExp("(hdw)123\\1","i");// \\1,代表把第一个原子组hdw,再次引用一遍; alert(reg.test("hdw123hdw"));//有反向引用的时候,必须是hdw123hdw才能匹配成功,没反向引用的时候hdw123既可 //字面量方式: var reg = /(hdw)(haha)123\2/i; //把第二个原子组再次引用一次; alert(reg.test("hdwhaha123haha")); //true; //可自定义原子分组名称: /\b(?<Word>\w+)\b\s+\k<Word>\b/ // /\b(\w+)\b\s+\1\b/ 命名:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)) 替换:\k<Word>或者\k'Word' ``` ## 断言匹配 用于查找在某些内容(**但并不包括这些内容**)之前或之后的东西,也就是说它们像`\b,^,$`那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。 断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。 ### 零宽断言 - `(?=exp)` 后面必须是以exp结束的 - `(?<=exp)` 前面必须是以exp开始的 ```javascript /\b\w+(?=ing\b)/ //匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc /(?<=\bre)\w+\b/ //会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。 /(?<=\s)\d+(?=\s)/ //匹配以空白符间隔的数字(再次强调,不包括这些空白符)。 ``` ```php $str3 = "nixi8.comjyhnixi8.cndsdf"; preg_match("/nixi8(?=\.com)/",$str3,$arr); /* 结果 Array ( [0] => nixi8 -> 断言匹配不包括断言部分; ) */ ``` ### 负向零宽断言 - `/(?!exp)/` 后面必须不是以exp结束的 - `/(?<!exp)/` 前面必须不是以exp开始的 ``` /\b\w*q[^u]\w*\b/ //匹配包含后面不是字母u的字母q的单词。但是如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。 /\d{3}(?!\d)/ //匹配三位数字,而且这三位数字的后面不能是数字; /\b((?!abc)\w)+\b/ //匹配不包含连续字符串abc的单词。 /(?<![a-z])\d{7}/ //匹配前面不是小写字母的七位数字。 /(?<=<(\w+)>).*(?=<\/\1>)/ //不包含属性的简单HTML标签内里的内容,(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身) ``` ### 量词 可以使用一些元字符,重复表示一些原子或元字符 * 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 > 量词的匹配默认情况下是尽量多的匹配,也即是贪婪匹配,变贪婪匹配为吝啬匹配可以用下面的方式;也就是对有选择匹配多或少的情况下再加一个问号,代表尽量少的匹配; *? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复 ```javascript var reg = /a.*b/; var reg1 = /a.*?b/; var str = 'assbaab'; //reg匹配全部,reg1匹配'assb' ``` ## 限定匹配的边界 ^ 匹配字符串的开始 $ 匹配字符串的结束,忽略换行符 \b 匹配单词的边界 \B 匹配除单词边界以外的边界部分 ```javascript //构造函数方式: var reg = new RegExp("(hdw)123\\1","i");// \\1,代表把第一个原子组hdw,再次引用一遍; alert(reg.test("hdw123hdw"));//有反向引用的时候,必须是hdw123hdw才能匹配成功,没反向引用的时候hdw123既可 //字面量方式: var reg = /here/i; alert(reg.test("hereaaa is a word"))//true; //\b的匹配 var reg = /\bhere\b/i; //加上\b边界符,代表匹配整个单词,可以理解为单词前后需要有空格; alert(reg.test("hereaaa is a word"))//false; alert(reg.test("here is a word"))//true; var reg = /\Bhere\B/i; //加上\B边界符,代表匹配单词前后非字母的; alert(reg.test("hereaaa is a word"))//false; alert(reg.test("2here5 is a word"))//true; var reg = /\bhi\b.*\bLucy\b/i; //先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。 var reg = /\ba\w*\b/ //匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。 var reg = /\b\w{6}\b/ //匹配刚好6个字符的单词。 /\b(\w+)\b\s+\1\b/ //可以用来匹配重复的单词,像go go, 或者kitty kitty。 ``` ## 模式修正符 i 不区分大小写字母的匹配 m 将字符串视为多行,匹配的字符串默认是一行,如果加了m的这个修饰符的话,^和$的意义就变成了匹配行的开始处和结束处。 g 全局匹配,找到所有匹配项,注意在php中g的模式是没有的,replace默认就是全部匹配的,preg_match_all为全匹配; x 模式中的空白忽略不计 U 匹配到最近的字符串 e 将替换的字符串作为表达使用 ```php $auth = '喂喂??嘿嘿??'; $query = preg_replace('/^.+\?/U','',$auth); //?嘿嘿?? 不加U 全部替换掉了; ``` ## 字符转义 要匹配在正则中已有意义的符号 `. * [ ] $ ( ) + ? \ ^ { } |`,就必须全部加上 `\`; > 注意↑↑,如果是在原子表或原子分组中, `. ?` 不用加上`\`; ## 注释 另外,小括号的另一种用途是通过语法(?#comment)来包含注释。例如: 2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。 再匹配的时候启用“忽略模式里的空白符”选项 可以匹配到这种更详细注释的↓↓ ``` / (?<= # 断言要匹配的文本的前缀 <(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签) ) # 前缀结束 .* # 匹配任意文本 (?= # 断言要匹配的文本的后缀 <\/\1> # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签 ) # 后缀结束 /x ``` 常用正则例子: ``` /0\d{2}-\d{8}/ //以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字.\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。 /\(?0\d{2}[) -]?\d{8}/ //首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8}),但是它也有可能匹配错误的字符串 010)12345678或(022-87654321;所以要用分支条件 | /0\d{2}-\d{8}|0\d{3}-\d{7}/ //主要匹配两种以连字号分隔的电话号码:一种是以0开头的三位区号,8位本地号(如010-12345678),一种是以0开头的4位区号,7位本地号(0376-2233445)。 /\(?0\d{2}\)?[- ]?\d{8}|0\d{2}[- ]?\d{8}/ //匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。 //使用分支条件在匹配时注意匹配的顺序,因为一旦匹配满足就会停止匹配; /\d{5}-\d{4}|\d{5}/ //美国邮编的规则是5位数字,或者用连字号间隔的9位数字,如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。 //限定原子组,匹配多个字符; /(\d{1,3}\.){3}\d{1,3}/ //简单的IP地址匹配,要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3}),不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址; /((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/ //01.02.03.04这个其实也是 ``` ## 不同编程语言对应的正则操作 ### javascript部分 #### 创建 1.通过构造函数创建 reg=new RegExp("正则表达式”,"模式修正符") ```javascript var reg = new RegExp("houdun"); var stat = reg.test("houdunwang"); //返回的是布尔值,成功就为true; alert(stat); ``` 2.通过字面量方式创建 ```javascript var reg = /houdun/i; //注意没有双引号 var stat = reg.test("houdunwang"); alert(stat); ``` #### 配对方法 1.test方法 `RegExp.test(str)` – 返回一个 Boolean 值,它指出在被查找的字符串中是否存在模式 2.exec方法 `RegExp.exec()` - 在字符串中匹配正则,成功返回数组,失败返回null 数组包含的属性: - input -被匹配的字符串 - index -子串位置 如果正则表达式没有设置g,那么exec方法不会对正则表达式有任何的影响,如果设置了g,那么exec执行之后会更新正则表达式的lastIndex属性,表示本次匹配后,所匹配字符串的下一个字符的索引,下一次再用这个正则表达式匹配字符串的时候就会从上次的lastIndex属性开始匹配。 ```javascript var str = "Visit W3School, W3School is a place to study web technology."; var patt = new RegExp("W3School","g"); //有设置g,代表全局索引,可以用循环来重复匹配;如果没有g,那只能配对一个; var result; //result = patt.exec(str); //console.log('result',result); //result ["W3School", index: 6, input: "Visit W3School, W3School is a place to study web technology."] while ((result = patt.exec(str)) != null) { document.write(result); document.write("<br />"); document.write(patt.lastIndex); document.write("<br />"); } /* ↑↑反复从str匹配patt: W3School 14 W3School 24 */ ``` #### 字符串处理 > str.search(regexp) regexp为正则表达式,反回索引位置,不支持全局索引(即g修饰符无效)找到即停止搜索 ```javascript var str = "www.houdunwang.com"; alert(str.search(/hou/)); //4,返回找到的位置; ``` > str.replace(正则或字符串,替换的新内容) 支持全局g修饰符,如果模式不是全局,当匹配到一个以后将不会继续匹配,反之则会继续往下匹配。返回替换后的字符串; ``` var str = "www.houdunwang.com"; var preg = /w{3}/; alert(str.replace(preg,'bbs')); //把www.houdunwang.com替换成bbs.houdunwang.com; var str = "you haha,i haha"; var preg = /haha/g; //加上全局g以后,可反复匹配,不加上g就只能匹配一个,匹配到就停止匹配; alert(str.replace(preg,'wuwu')); ``` > str.split(字符串或正则,[限定返回的数组元素个数]) 拆分字符串,参数可以为字符串或正则表达式 ```javascript var str = "zhou@xiao&gang#haha"; var preg = /[@&#]/; var r = str.split(preg); console.log('str',r);//["zhou", "xiao", "gang", "haha"] ``` ### php部分 一,创建 ```php "/reg/" '#reg#' '@reg@' //和js不同,必须要加引号 ``` 二,配对方法 `int|array preg_match( 正则, 被匹配的字符串 [, array matches ] )` 关于第三参数:可选,存储匹配结果的数组,$matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推 ```php $preg = "/haha/"; $str = "you haha,my haha"; $r = preg_match($preg,$str); //成功返回1,失败返回0 preg_match("/(zh.*u)\s*(g.*g)/", "zhou ganggang", $matches); print_r($matches);//Array ( [0] => zhou ganggang [1] => zhou [2] => ganggang ),0是与整个模式匹配的结果,1是第一个括号,2是第二括号..... $url1 = "http://www.126.com"; $url2 = "http://www.sina.com.cn"; $preg = "/http:\/\/www\.\w+\.(com\.cn|com)/i"; preg_match($preg,$url2,$arr);//把$url2与正则匹配的结果返回给$arr; print_r($arr);//Array ( [0] => http://www.sina.com.cn [1] => com.cn ) ``` `preg_match_all($preg,$str,$arr)` //preg_match满足一次就停止匹配,而preg_match_all会重复匹配; ```php $str='网站:http://www.hdw.com;论坛:http://bbs.hdw.com;shop.hdw.cn;域名hdw.com'; $preg='/(?<!http:\/\/)(?:\w*)\.?\w+\.(?:com\.cn|com|cn)/is'; preg_match_all($preg,$str,$arr); /*array的结果 Array( [0] => Array ( [0] => ww.hdw.com -> www.hdw.com,要从第二个字符开始才满足匹配条件,定义第一个字符就会不出现; [1] => bs.hdw.com [2] => shop.hdw.cn [3] => hdw.com ) ) */ ``` 三,字符串处理 1. `preg_split($preg,$str);` 用\$preg分割\$str,返回分割结果的数组; ```php $str ="1.jpg@2.jpg@3.jpg#4.jpg"; $preg="/[@#]/"; $arr = preg_split($preg,$str); print_r($arr);//返回其数组; ``` 2\. preg_replace(正则,要替换的,被替换的);返回替换后的字符串; ```php $str = "网站www.nixi8.com论坛bbs.nixi8.com网名NIXI8"; $preg = "/(nixi8)/i"; $new_str = preg_replace($preg,"<span style='color:red'>\\1</span>",$str);//把上面的nixi8描红,本函数与js略有不同,不用加g就是全局匹配;并且\\1必须是两斜杠; $str = "后盾官网http://www.houdunwang.com后盾论坛https://bbs.houdunwang.com"; $preg = "/(?<!http:\/\/)(?:bbs|www)\.(houdunwang)\.com/isU"; $new_str = preg_replace($preg,'\\1.com',$str);//匹配所有不以http//开始的,bbs或者www.houdunwang.com -忽略大写写i,忽略空白s,匹配到最近的字符串U;然后替换成houdunwang.com echo $new_str; #为没有http://的网址加上http:// ↓↓ $str='网站:http://www.nixi8.com;论坛:bbs.zxg.com;商城shop.zxg.cn;域名vip.zxg.com'; $preg='/(?<!http:\/\/)(?:www|bbs|shop|vip)\.?\w+\.(?:com\.cn|com|cn)/i'; $result = preg_replace($preg,"http://\\0",$str); ``` 3\. `array preg_grep ( string $pattern , array $input [, int $flags = 0 ] )` ```php $subjects = array( "Mechanical Engineering", "Medicine", "Social Science", "Agriculture", "Commercial Science", "Politics" ); $alonewords = preg_grep("/^[a-z]*$/i",$subjects);//匹配以字母开头并且以字母结束的字符串,不忽略空格,所以^$在遇到空格的时候就会生效;返回的是一个数组,保持原有的索引;有第三参数0|1,默认为0,如果为1的时候就返回相反的结果,也就是不匹配时的结果; print_r($alonewords); /* Array ( [1] => Medicine [3] => Agriculture [5] => Politics ) */ ``` 4\. `mixed preg_replace_callback ( mixed pattern, callback callback, mixed subject [, int limit] )` 可以看做是一个有条件处理的preg_replace函数; ```php $text = "April fools day is 04/01/2002\n"; $text.= "Last christmas was 12/24/2001\n"; function next_year($matches) { // 通常:$matches[0] 是完整的匹配项 // $matches[1] 是第一个括号中的子模式的匹配项 // 以此类推 return $matches[1].($matches[2]+1); } echo preg_replace_callback( "|(\d{2}/\d{2}/)(\d{4})|", "next_year", $text); //April fools day is 04/01/2003 Last christmas was 12/24/2002 ```