Regexp: Regular Expression

三要素

  1. 字符串表达式
  2. Pattern模板对象
  3. Matcher匹配器

    1. public class RegexpDemo {
    2. public static void main(String[] args) {
    3. String regex = "regex";
    4. String content = "content";
    5. // 获取表达式对应的模板对象,区分大小写
    6. Pattern pattern = Pattern.compile(regex);
    7. // 获取表达式对应的模板对象,不区分大小写
    8. Pattern caseInsensitivePattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
    9. // 直接判断表达式是否整体匹配内容,相当于表达式前后强制加了定位符^$
    10. boolean isMatch = Pattern.matches(regex, content);
    11. Matcher matcher = pattern.matcher(content);
    12. while (matcher.find()) {
    13. System.out.println(matcher.group(0));
    14. }
    15. }
    16. }

    Matcher匹配器源码解析

    1. public final class Matcher implements MatchResult {
    2. /**
    3. * 存储字符串索引值得容器
    4. */
    5. int[] groups;
    6. /**
    7. * 最后一次匹配操作中匹配的对象的结束索引+1
    8. */
    9. int oldLast = -1;
    10. public boolean find() {
    11. //源码...
    12. }
    13. public String group(int group) {
    14. if (first < 0)
    15. throw new IllegalStateException("No match found");
    16. if (group < 0 || group > groupCount())
    17. throw new IndexOutOfBoundsException("No group " + group);
    18. if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
    19. return null;
    20. return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
    21. }
    22. }

    执行 find() 方法解析

  4. 匹配器根据模板对象的规则,定位满足规则的子字符串

  5. 将匹配的子字符串记录到groups
    1. 数组的默认值为-1
    2. groups[0]记录子字符串开始索引,groups[1]oldLast记录子字符串的结束索引+1
    3. 如果有分组,即表达式中设置了(),则groups[2]记录分组1的开始索引,groups[3]记录分组1的结束索引+1,groups[4]记录分组2的开始索引,groups[5]记录分组2的结束索引+1,如果有更多的分组,依此类推
  6. 在下次执行find()时,匹配器将从从oldLast开始匹配

    执行 group(0) 方法解析

  7. 根据groups[0]groups[1]记录的索引位置截取字符串获得相应的子字符串

  8. 字符串sub(截取)规则:[groups[0], groups[1]),包含groups[0],不包含groups[1]
  9. 参考字符串截取规则,可以理解groups[1]为什么记录的是子字符串的结束索引+1

    相关方法

    | 方法 | 描述 | | —- | —- | | boolean find() | 尝试查找与该表达式匹配的输入序列的下一个子字符串 | | boolean find(int start) | 重置此匹配器,然后尝试查找匹配该表达式、从指定索引开始的输入序列的下一个子字符串 | | boolean matches() | 尝试将整个字符串与表达式匹配,相当于表达式前后强制加了定位符^$ | | boolean lookingAt() | 尝试从整个字符串的开头位置开始匹配表达式的匹配结果 | | int start() | 返回上次匹配子字符串初始索引 | | int start(int group) | 返回在上次的匹配操作期间,由给定组所捕获的子序列的初始索引 | | int end() | 返回上次匹配子字符串结束索引+1 | | int end(int group) | 返回在上次的匹配操作期间,由给定组所捕获子字符串结束索引+1 | | Matcher appendReplacement(StringBuffer sb, String replacement) | 实现非终端添加和替换步骤 | | StringBuffer appendTail(StringBuffer sb) | 实现终端添加和替换步骤 | | String replaceAll(String replacement) | 使用给定字符串替换表达式匹配的每个子字符串 | | String replaceFirst(String replacement) | 使用给定字符串替换表达式匹配的第一个子字符串 | | static String quoteReplacement(String s) | 返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement方法一个字面字符串一样工作 |

元字符

转义符

需要用到转义符的字符有:.*+()$/\?[]^{}
在Java中的转义符是\\,其他语言是\

字符匹配符

Java的字符匹配符默认区分大小写,改变方式:

  1. 通过(?i)改为不区分大小写。例如(?i)abc可以匹配abc或ABC,a(?i)bc可以匹配bc或BC,a(?)b)c可以匹配b或B
  2. 通过设置参数不区分大小写。例如Pattern._compile(_regStr, Pattern._CASE_INSENSITIVE)_; | 符号 | 定义 | 模板示例 | 示例说明 | 命中示例 | | —- | —- | —- | —- | —- | | [] | 可接收的字符列表 | [efgh] | e、f、g、h中的任意1个字符 | | | [^] | 不可接收的字符列表 | [^abc] | 除a、b、c之外的任意1个字符,包括数字和特殊符号 | | | - | 连字符 | A-Z | 任意1个大写字母 | | | . | 匹配除\n以外的任何字符,如果要匹配.本身需要使用\\. | a..b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 | aaab、aefb、a35b、a#b | | \\d | 匹配1个数字字符,相当于[0-9] | \\d{3}(\\d)? | 包含3个或4个数字的字符串 | 123、9876 | | \\D | 匹配1个非数字字符,相当于[^0-9] | \\D(\\d) | 以1个非数字字符开头,后接任意个数数字字符串 | a、A342 | | \\w | 匹配1个数字、大小写字母字符、下划线,相当于[0-9a-zA-Z] | \\d{3}\\w{4} | 以3个数字字符开头的长度为7的数字字母字符串 | 1_2 abcd、12345Ef | | \\W | 匹配1个非数字、大小写字母字符、下划线,相当于[^0-9a-zA-Z] | \\W+\\d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串 | #29、#?@10 | | \\s | 匹配任何空白字符(空格,制表符等) | | | | | \\S | 匹配任何非空白字符 | | | |

选择匹配符

多个表达式之间的关系选择

符号 定义 模板示例 示例说明 命中示例
| 匹配”|”之前或之后的表达式 ab|cd|ef ab或cd或ef

限定符

用于指定其前面的字符或组合项重复次数

符号 定义 模板示例 示例说明 命中示例
* 指定字符重复任意次 (abc)* 仅包含任意个abc的字符串 abc、abcabcabc
+ 指定字符重复至少1次,默认贪婪匹配 m+(abc)* 以至少1个m开头,后接任意个abc的字符串 m、mabc、mabcabc
? 指定字符重复最多1次 m+abc? 以至少1个m开头,后接最多一次c的字符 m、mab、mabc、mmabc
{n} 指定字符重复n次 [abcd]{3} 由abcd中字母组成的任意长度为3的字符串 aac、dbc、adc
{n,} 指定字符重复至少n次,默认贪婪匹配 [abcd]{3,} 由abcd中字母组成的任意长度不小于3的字符串 aab、dbc、aaabdc
{n,m} 指定字符重复n次到m次,默认贪婪匹配 [abcd]{3,5} 由abcd中字母组成长度3到5的字符串 abc、abcd、aaaaa、bcdab
? 当此字符紧随其他限定符之后使用时,匹配模式改为非贪婪匹配,即尽可能匹配短的字符串。 o+? 只匹配单个”o” oooo

定位符

规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置

符号 定义 模板示例 示例说明 命中示例
^ 指定起始字符 ^[0-9]+[a-z]* 以至少1个数字开头,后接任意个小写字母的字符串 123、6aa、555edf
$ 指定结束字符 ^[0-9]\\-[a-z]+$ 以1个数字开头后接连字符”-“,并以至少1个小写字母结尾的字符串 1-a
\\b 匹配目标字符串的边界 ban\\b 边界指的是字串间有空格,或目标字符串的结束位置 bandaoban nnban
\\B 匹配目标字符串的非边界 ban\\B bandaoban nnban

分组

捕获分组

构造形式 说明
(pattern) 非命名捕获。捕获匹配的子字符串。group(0)为第1个捕获结果,为整个正则表达式模板匹配的文本,其他捕获结果group(1)、group(2)等根据左括号的顺序开始自动编号
(?pattern)

(?’name’pattern)
命名捕获。既可以用编号获取,也可以用命名获取。用于name的字符串不能包含任何标点符号,并且不能以数字开头

非捕获分组

非捕获分组匹配,不会存储到分组中,更经济,更节省资源,同时不可用group(1)等方式去捕获,否则会报错

构造形式 说明
(?:pattern) 此构造常用于替换(|)的分组形式。例如,”industr(?:y|ies)表达式”比”industry|industries”更经济
(?=pattern) 此构造常用于匹配需要在明确范围内的分组形式。例如,”Windows(?=95|98|NT|2000)”匹配”Windows2000”中的”Windows”,但不匹配”Windows3.1”中的”Windows”
(?!pattern) 此构造常用于匹配需要在明确范围外的分组形式。例如,”Windows(?!95|98|NT|2000)”匹配”Windows3.1”中的”Windows”,但不匹配”Windows2000”中的”Windows”

反向引用

当分组捕获后,分组号可以被反向引用,如果在表达式内部使用则采用”\分组号”的形式,如果在表达式外部使用则采用”$分组号”的形式。

构造形式 说明
(pattern)\\1{4} 连续匹配五个相同的子字符串
(pattern)(pattern)\\2\\1 第一个字符串与第四个子字符串相同,第二个子字符串与第三个子字符串相同

String类中使用正则表达式

相关方法

方法 说明
String replaceAll(String regex, String replacement) 替换符合表达式的子字符串
String[] split(String regex) 按符合表达式的子字符串进行分割
boolean matches(String regex) 判断整个字符串是否匹配表达式