本文github地址:https://github.com/clover402/one-piece/blob/master/java/regex.md
一般讲解正则表达式都会按照技术的顺序去讲解,往往会比较枯燥,我决定换一个顺序,从使用场景来讲。
一般使用Pattern.matches(String regex, CharSequence input)方法,该方法返回的是boolean值,这个方法会判断input字符串是否符合设定的regex正则表达式。
String content = "I am noob from runoob.com.";
String pattern = ".*runoob.*";
boolean isMatch = Pattern.matches(pattern, content);
下面进入正题讲下会用到的正则表达式的语法
需求 | 正则表达式 | 说明 |
---|---|---|
数字 | \d 或 [0-9] | 匹配任意一个0-9的数字,说明\是转义符,\加字母可以表示很多特殊的含义。[xyz]表示字符集,匹配包含的任一字符 |
非数字 | \D 或 [^0-9] | 匹配任意一个不是0-9的字符,在[]中表示取反,与逻辑运算符!含义一样 |
字母 | [a-zA-Z] | [x-y]中-连接表示范围,目前支持的就3种a-z,A-Z,0-9,及其子集,比如0-5,a-m |
字类字符 | \w 或 [a-zA-Z0-9_] | 实际上就是C语言要求的变量名包含的字符范围,正则起源于Unix与C语言颇有渊源 |
非字类字符 | \W 或 [^a-zA-Z0-9_] | 发现没有它这里转义符有个规律,\跟小写字母是包含某些字符,\跟对应的大写是不包含某些字符 |
是否 | true|false | |用来分隔多个可选项 |
回车符 | \r | ASCII中为x0D,windows的换行由\r\n构成 |
换行符 | \n | ASCII中为x0A,linux的换行只有\n |
制表符 | \t | 相当于Tab键的效果 |
垂直制表符 | \v | 这个比较少见 |
空白字符 | \s 或 [ \f\n\r\t\v] | 包括空格、制表符、换页符(\f)等 |
非空字符 | \S 或 [^ \f\n\r\t\v] | 这个比较常用 |
任意字符 | [\S\s] | 正反合二为一就构成了整个世界 |
非换行字符 | . | 除了\r\n任意字符,比较常用 |
中国手机号 | [\d]{11} | 表示11位数字,后面的大括号中的数字表示前面内容出现的次数,这里是固定11次 |
大于99的整数 | [\d]{3,} | 表示3位及以上的数字 |
QQ号码 | 1\d{4,10}$ | 表示数字出现4到10次 |
任意单词 | [a-zA-Z]+ | 通配符+表示一次或多次匹配前面的字符或子表达式(>=1) |
c语言变量命名规范 | [a-zA-Z_]\w* | 以字母或下划线开头,由字母数字下划线构成。其中通配符*表示0次或多次配前面的内容(>=0) |
URL | ^((http|https)?/)?([\w-]+.)+[\w-]+(/[\w-./?%&=]*)?$ | 通配符?表示前面的表达式出现0次或1次(0||1) |
一个或多个汉字 | 2+$ | ^在外面表示字符串开始,$表示字符串结束 |
最后一个特殊的\表示转义符,一些特殊符号*.?{}|^$\等如果需要作为字符存在则可以在他们前面加\表示该字符。但java中有点特殊,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释,所以\d 需要\\d表示,\\需要\\\\表示。
还有些特殊用法,贪婪与懒惰
语法 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
一般用于从一个字符串中解析出特定的部分,比如表达式的解析等。还有就是爬虫字符串的解析,比如图片url链接url等。Java中使用示例如下
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");
}
其中m.group(0)代表整个表达,m.group(1)、m.group(2)、m.group(3)分别表示第一二三个括号中批判的内容
上面的运行结果如下
Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT
Found value: 3000
Found value: ! OK?
以上可看出小括号在正则中的特殊用法。但小括号最基本的用法是表示括号中的内容是一个整体。
捕获方法还有更高级的用法,通过group数组的方式取捕获的内容存在一定的不可靠性,java还支持给每个组取名,然后通过名称来获取捕获内容
Pattern AGGREGATED_COLUMN_PATTERN = Pattern.compile("(?\\w+)\\((?[\\w#.]+)\\)" );
Matcher m = AGGREGATED_COLUMN_PATTERN.matcher(column);
if(m.matches()) {
return m.group("function");
}
(?X) X, as a named-capturing group,通过这种方式就可以给group组起名字了
此处的用法基本与匹配是一样的,把匹配到的字符串替换成对应的字符串
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
//也可以直接用String的replaceAll方法
String INPUT = "abc,123,efg";
INPUT.replaceAll("\\d","");
看源码你会发现其实两种方式是完全一样的,只是String的方法更方便。replaceFirst方法也是一样的。
分割字符串的时候也可以用正则表达式,这样能实现一些很强大的功能。可以看下Pattern类split方法的描述
Splits the given input sequence around matches of this pattern
String s = "123\\r\\n456 789";
String[] arr = s.split("\\s");
String类中的split方法也是通过Pattern的split方法实现的
Pattern更详细全面的用法可以参考java文档
参考文档
1-9 ↩︎
\u0391-\uFFE5 ↩︎