本文主要讲解了一些不太常用的正则表达式语法,并给出了一些自己写的(或找的)常见的一些正则表达式用于校验或是内容的提取,可能存在一些错误,最后给出了c语言、python、java如何使用正则表达式的语法。
一些正则表达式语法
符号 | 描述 |
---|---|
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 ^。 |
$ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。要匹配 $ 字符本身,请使用 \$。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 或 “does” 。? 等价于 {0,1}。 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。 |
(?=pattern) | 正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)“能匹配"Windows2000"中的"Windows”,但不能匹配"Windows3.1"中的"Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)“能匹配"Windows3.1"中的"Windows”,但不能匹配"Windows2000"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?<=pattern) | 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,"(?<=95|98|NT|2000)Windows"能匹配"2000Windows"中的"Windows",但不能匹配"3.1Windows"中的"Windows"。 |
(?<!pattern) | 反向否定预查,与正向否定预查类似,只是方向相反。例如"(?|98|NT|2000)Windows"能匹配"3.1Windows"中的"Windows",但不能匹配"2000Windows"中的"Windows"。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。 |
\B | 匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。 |
\D | 匹配一个非数字字符。等价于 [^0-9]。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
\w | 匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’。 |
\W | 匹配非字母、数字、下划线。等价于 [^A-Za-z0-9_]。 |
\un | 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。 |
常用的正则表达式
-
校验密码,要求密码为大小写字母或数字,且位数在8位到10位。
^[a-zA-Z0-9]{8,10}$
-
校验字符串,要求只能维中文。
^[\u4e00-\u9fa5]*$
-
校验字符串,要求只能是字母数字或下划线。
^\w+$
-
校验密码,要求只能是大小写字母或数字,且位数在6到16位,且不能是纯数字或纯字母
^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$
-
校验金额,要求为两位小数。
^[0-9]+(\.[0-9]{2})?$
-
从文本中提取url。
(f|ht){1}(tp|tps):\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?
-
提取网页中的图片。
< *[img][^\\>]*[src] *= *[\"\']{0,1}([^\"\'\\ >]*)
-
提取网页中的注释。
<!--(.*?)-->
代码书写
c语言
首先介绍一下一些函数接口。
pcre *pcre_compile(const char *pattern, int options,
const char **errptr, int *erroffset,
const unsigned char *tableptr);
功能:编译指定的正则表达式
参数:pattern, 输入参数,将要被编译的字符串形式的正则表达式
options, 输入参数,用来指定编译时的一些选项
errptr, 输出参数,用来输出错误信息
erroffset, 输出参数,pattern中出错位置的偏移量
tableptr, 输入参数,用来指定字符表,一般情况用NULL, 使用缺省的字符表
返回值:被编译好的正则表达式的pcre内部表示结构
int pcre_exec(const pcre *code, const pcre_extra *extra,
const char *subject, int length, int startoffset,
int options, int *ovector, int ovecsize);
功能:用来检查某个字符串是否与指定的正则表达式匹配
参数: code, 输入参数,用pcre_compile编译好的正则表达结构的指针
extra, 输入参数,用来向pcre_exec传一些额外的数据信息的结构的指针
subject, 输入参数,要被用来匹配的字符串
length, 输入参数, 要被用来匹配的字符串的指针
startoffset, 输入参数,用来指定subject从什么位置开始被匹配的偏移量
options, 输入参数, 用来指定匹配过程中的一些选项
ovector, 输出参数,用来返回匹配位置偏移量的数组
ovecsize, 输入参数, 用来返回匹配位置偏移量的数组的最大大小
返回值:匹配成功返回非负数,匹配返回负数
接下来举一个例子,判断给定一个字符串是否满足密码要求(只能包含字母和数字,并且位数在6位到16位,并且不能是纯字母或纯数字)。
#include<pcre.h>
#include<stdio.h>
int main(){
int ovector[10];
const char* err_ptr = NULL;
int err_offset = -1;
char check[] = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$";
pcre* re_check = pcre_compile(check, 0, &err_ptr, &err_offset, NULL);
if (re_check == NULL){
printf("error\n");
return 0;
}
int result = pcre_exec(re_check, NULL, "faf1234ci", 9, 0, 0, ovector, 10);
if(result > 0) {
printf("match\n");
printf("%d %d\n", ovector[0], ovector[1]);
}
else{
printf("not match\n");
}
result = pcre_exec(re_check, NULL, "12345611", 8, 0, 0, ovector, 10);
if(result > 0) {
printf("match\n");
printf("%d %d\n", ovector[0], ovector[1]);//0和1为匹配串的起始位置和结束位置
}
else {
printf("not match\n");
}
pcre_free(re_check);
return 0;
}
/*
输出为:
match
0 9
not match
*/
python
采用re模块进行正则式的匹配。
re.match函数
re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
函数语法:
re.match(pattern, string, flags=0)
函数参数说明:
参数 | 描述 |
---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志 |
匹配成功re.match方法返回一个匹配的对象,否则返回None。
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
匹配对象方法 | 描述 |
---|---|
group(num=0) | 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 |
可以用span()函数来获得匹配的位置。
match函数是从文本串开始进行匹配的,不管有没有^。
import re
pattern = "(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$"
print(re.match(pattern, "1234567712a"))
print(re.match(pattern, "1234567712a").span())
print(re.match(pattern, "123"))
#输出:
#<re.Match object; span=(0, 11), match='1234567712a'>
#(0, 11)
#None
re.search函数
re.search 扫描整个字符串并返回第一个成功的匹配。
函数语法:
re.search(pattern, string, flags=0)
函数参数说明:
参数 | 描述 |
---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
匹配成功re.search方法返回一个匹配的对象,否则返回None。
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
匹配对象方法 | 描述 |
---|---|
group(num=0) | 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 |
#匹配大小写字母
import re
pattern = re.compile("[a-z]", re.I)
print(re.search(pattern, "a"))
print(re.search(pattern, "12ZZ"))
print(re.search(pattern, "123"))
#输出:<re.Match object; span=(0, 1), match='a'>
#<re.Match object; span=(2, 3), match='Z'>
#None
re.findall函数
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次 findall 匹配所有。
语法格式为:
findall(string[, pos[, endpos]])
参数:
- string : 待匹配的字符串。
- pos : 可选参数,指定字符串的起始位置,默认为 0。
- endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。
import re
pattern = "[a-z]+"
print(re.findall(pattern, "a"))
print(re.findall(pattern, "12zz123j"))
print(re.findall(pattern, "123"))
#输出:['a']
#['zz', 'j']
#[]
re.compile函数
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
语法格式为:
re.compile(pattern[, flags])
参数:
- pattern : 一个字符串形式的正则表达式
- flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:
- re.I 忽略大小写
- re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
- re.M 多行模式
- re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
- re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
- re.X 为了增加可读性,忽略空格和 # 后面的注释
java
java.util.regex 包主要包括以下三个类:
-
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
-
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
-
PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
第一个例子同样是进行密码的校验,采用Pattern.matches函数,返回值为布尔值。
import java.util.regex.*;
public class Test {
public static void main(String[] args) {
String pattern = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$";
boolean isMatch = Pattern.matches(pattern, "1234144141");
boolean isMatch2 = Pattern.matches(pattern, "1239afc");
System.out.println(isMatch+" "+isMatch2);
}
}
/*结果:
false true
*/
第二个例子采用和python或c类似的compile函数,然后利用matcher函数进行匹配,和python类似,java也含有group函数。如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test
{
public static void main( String args[] ){
// 按指定模式在字符串查找
String line = "This order was placed for QT3000! OK?";
String pattern = "(\\D*)(\\d+)(.*)";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
System.out.println("Found value: " + m.group(2) );
System.out.println("Found value: " + m.group(3) );
} else {
System.out.println("NO MATCH");
}
}
}
/*结果:
Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT
Found value: 3000
Found value: ! OK?
*/