跳至主要內容

Java 正则的使用

大约 17 分钟

Java 正则的使用

一、正则表达式详解

正则表达式是由普通字符(如英文字母)以及特殊字符(也称为元字符)组成的文字模式。该模式对文本查找时需要匹配的一个或多个字符串描述,给出一个匹配模板。它专门用于操作字符串,可以简化对字符串的复杂操作。

以下是一些使用正则表达式的主要原因:

  1. 匹配和验证文本:正则表达式可以用于验证和匹配文本,例如电子邮件地址、电话号码、网址等。通过使用正则表达式,可以快速准确地确定字符串是否符合特定的格式要求。
  2. 搜索和替换文本:正则表达式可以用于搜索和替换文本中的特定模式。例如,可以使用正则表达式搜索包含特定关键字的文件或文本,并将其替换为其他内容。
  3. 数据提取:正则表达式可以用于从文本中提取特定的数据,例如从网页中提取电子邮件地址、电话号码等。通过使用正则表达式,可以快速准确地提取所需的数据。
  4. 自动化处理:正则表达式可以用于自动化处理文本,例如自动生成代码、批量更改文件名、批量处理数据等。

总之,正则表达式是一种非常强大和灵活的文本处理工具,可以极大地提高处理文本的效率和准确性。

1.1 符号定义

1.1.1 基本书写符号

符号符号示例解释匹配输入
\转义符*符号“*”*
[ ]可接收的字符列表[efgh]e、f、g、h中的任意1个字符e、f、g、h
[^]不接收的字符列表[^abc]除a、b、c之外的任意1个字符,包括数字和特殊符号m、q、5、*
|匹配“|”之前或之后的表达式ab|cdab或者cdab、cd
( )将子表达式分组(abc)将字符串abc作为一组abc
-连字符A-Z任意单个大写字母大写字母

1.1.2 限定符

限定符将可选数量的数据添加到正则表达式,下表为常用限定符:

符号含义示例示例
*指定字符重复0次或n次(abc)*仅包含任意个abc的字符串,等效于\w*
+指定字符重复1次或n次m+(abc)*以至少1个m开头,后接任意个abc的字符串
?指定字符重复0次或1次m+abc?以至少1个m开头,后接ab或abc的字符串
{n}只能输入n个字符[abcd]{3}由abcd中字母组成的任意长度为3的字符串
{n,}指定至少 n 个匹配[abcd]{3,}由abcd中字母组成的任意长度不小于3的字符串
{n,m}指定至少 n 个但不多于 m 个匹配[abcd]{3,5}由abcd中字母组成的任意长度不小于3,不大于5的字符串
^指定起始字符^[0-9]+[a-z]*以至少1个数字开头,后接任意个小写字母的字符串
$指定结束字符^[0-9]-[a-z]+$以1个数字开头后接连字符“–”,并以至少1个小写字母结尾的字符串

1.1.3 匹配字符集

匹配字符集是预定义的用于正则表达式中的符号集。如果字符串与字符集中的任何一个字符相匹配,它就会找到这个匹配项。

正则表达式中的部分匹配字符集:

常用分组构造形式说明
()非命名捕获。捕获匹配的子字符串(或非捕获组)。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号。
(?<name>)命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如 (?‘name’)

1.1.4 字符转义

如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用.和*。当然,要查找\本身,你也得用\

例如:deerchao.net匹配deerchao.NET,C:\Windows匹配C:\Windows。注意在Java中: (https://github\.com/[\w\-]) 用"\.“配备”."。

1.2 常用正则表达式举例

非负整数:“^\d+$ ”
正整数: “ ^[0-9]*[1-9][0-9]*$” 
非正整数: “ ^((-\d+)|(0+))$” 
整数: “ ^-?\d+$” 
英文字符串: “ ^[A-Za-z]+$” 
英文字符数字串: “ ^[A-Za-z0-9]+$” 
英数字加下划线串: “^\w+$” 
E-mail地址:“^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$” 
URL:“^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$” 

Java 常用正则表达式(数字,字符串处理)
匹配特定数字
^[1-9]d*$    //匹配正整数
^-[1-9]d*$   //匹配负整数
^-?[1-9]d*$   //匹配整数
^[1-9]d*|0$  //匹配非负整数(正整数 + 0)
^-[1-9]d*|0$   //匹配非正整数(负整数 + 0)
^[1-9]d*.d*|0.d*[1-9]d*$   //匹配正浮点数
^-([1-9]d*.d*|0.d*[1-9]d*)$  //匹配负浮点数
^-?([1-9]d*.d*|0.d*[1-9]d*|0?.0+|0)$  //匹配浮点数
^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0$   //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]d*.d*|0.d*[1-9]d*))|0?.0+|0$  //匹配非正浮点数(负浮点数 + 0)

匹配特定字符串
^[A-Za-z]+$  //匹配由26个英文字母组成的字符串
^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串
^[a-z]+$  //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串
^w+$  //匹配由数字、26个英文字母或者下划线组成的字符串

验证Email地址:“^w+[-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$”
验证InternetURL:“^http://([w-]+.)+[w-]+(/[w-./?%&=]*)?$”
验证电话号码:“^((d{3,4})|d{3,4}-)?d{7,8}$”
验证身份证号(15位或18位数字):“^d{15}|d{}18$”
验证一年的12个月:“^(0?[1-9]|1[0-2])$”正确格式为:“01-09”和“1”“12”
验证一个月的31天:“^((0?[1-9])|((1|2)[0-9])|30|31)$”
匹配中文字符的正则表达式: [u4e00-u9fa5]
匹配双字节字符(包括汉字在内)[^x00-xff]
匹配空行的正则表达式:n[s| ]*r
匹配HTML标记的正则表达式:/< (.*)>.*|< (.*) />/
匹配首尾空格的正则表达式:(^s*)|(s*$)
匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
匹配网址URL的正则表达式:http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?

Java这些常用的正则表达式在处理大数据查找,更新,替换的时候可以极大的提高效率。

1.3 Java中的RegularExpressionValidator

Java使用RegularExpressionValidator验证控件时,它的验证功能及其验证正则表达式如下:

只能输入数字:“^[0-9]*$”
只能输入n位的数字:“^d{n}$”
只能输入至少n位数字:“^d{n,}$”
只能输入m-n位的数字:“^d{m,n}$”
只能输入零和非零开头的数字:“^(0|[1-9][0-9]*)$”
只能输入有两位小数的正实数:“^[0-9]+(.[0-9]{2})?$”
只能输入有1-3位小数的正实数:“^[0-9]+(.[0-9]{1,3})?$”
只能输入非零的正整数:“^+?[1-9][0-9]*$”
只能输入非零的负整数:“^-[1-9][0-9]*$”
只能输入长度为3的字符:“^.{3}$”
只能输入由26个英文字母组成的字符串:“^[A-Za-z]+$”
只能输入由26个大写英文字母组成的字符串:“^[A-Z]+$”
只能输入由26个小写英文字母组成的字符串:“^[a-z]+$”
只能输入由数字和26个英文字母组成的字符串:“^[A-Za-z0-9]+$”
只能输入由数字、26个英文字母或者下划线组成的字符串:“^w+$”


验证用户密码:“^[a-zA-Z]w{5,17}$”正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。
只能输入汉字:“^[u4e00-u9fa5],{0,}$”
验证Email地址:“^w+[-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$”
验证InternetURL:“^http://([w-]+.)+[w-]+(/[w-./?%&=]*)?$”
验证电话号码:“^((d{3,4})|d{3,4}-)?d{7,8}$”
验证身份证号(15位或18位数字):“^d{15}|d{}18$”
验证一年的12个月:“^(0?[1-9]|1[0-2])$”正确格式为:“01”-“09”和“1”“12”
验证一个月的31天:“^((0?[1-9])|((1|2)[0-9])|30|31)$”


匹配中文字符的正则表达式: [u4e00-u9fa5]
匹配双字节字符(包括汉字在内):[^x00-xff]
匹配空行的正则表达式:n[s| ]*r
匹配HTML标记的正则表达式:/< (.*)>.*|< (.*) />/
匹配首尾空格的正则表达式:(^s*)|(s*$)
匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
匹配网址URL的正则表达式:http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?

1.4 正则表达式匹配简单语法汇总

1、字母:匹配单个字母
A:表示匹配字母A;
\\:匹配转义字符“\”;
\t:匹配转义字符“\t”;
\n:匹配转义字符“\n”;

2、一组字符:任意匹配里面的一个单个字符
[abc]:表示可能是字母a,可能是字母b或者是字母c;
[^abc]:表示不是字母a,字母b,字母c的任意一个;
[a-zA-Z]:表示全部字母中的任意一个;
[0-9]:表示全部数字的任意一个;

3、边界匹配
^:表示一组正则的开始;
$:表示一组正则的结束;

4、简写表达式:每一位出现的简写标记也只表示一位
· :表示任意的一位字符;
\d :表示任意的一位数字,等价于“[0-9]”;
\D :表示任意的一位非数字,等价于“[~0-9]”;
\w :表示任意的一位字母、数字、_,等价于“[a-zA-Z0-9_]”;
\w :表示任意的一位非字母、数字、_,等价于“[^a-zA-Z0-9_]”;
\s :表示任意的一位空格,例如:\n、\t等;
\S :表示任意的一位非空格;
5、数量表示:之前所有的正则都只是表示一位,如果要表示多位,则就需要数量表示。
?:此正则出现0次或1次;
*:此正则出现0次、1次或多次;
+:次正则出现1次或多次;
{n}:此正则出现正好n次;
{n,}:此正则出现n次以上;
{n,m}:此正则出现n – m次。
6、逻辑表示:与、或、非
正则表达式A正则表达式B: 表达式A之后紧跟着表达式B;
正则表达式|A正则表达式B: 表示表达式A或者表达式B,二者任选一个出现;
(正则表达式):将多个子表达式合成一个表示,作为一组出现。

二、Pattern类详解

Pattern在java.util.regex包中,是正则表达式的编译表示形式,此类的实例是不可变的,可供多个并发线程安全使用。

2.1 获取Pattern实例

Pattern的构造器被设计为私有,不允许通过new的方式创建Pattern。

private Pattern(String p, int f) {
    pattern = p;
    flags = f;

    // to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
    if ((flags & UNICODE_CHARACTER_CLASS) != 0)
        flags |= UNICODE_CASE;
    
    // Reset group index count
    capturingGroupCount = 1;
    localCount = 0;
    
    if (pattern.length() > 0) {
        compile();
    } else {
        root = new Start(lastAccept);
        matchRoot = lastAccept;
    }

}

要想获取Pattern的实例,可以使用其静态方法获取,将给定正则表达式编译为具有给定标志的模式。参数:

  • regex -要编译的表达式

  • flags -匹配标志,位掩码,可以包括CASE_INSENSITIVE, MULTILINE, DOTALL, UNICODE_CASE, CANON_EQ, UNIX_LINES, LITERAL, UNICODE_CHARACTER_CLASS和COMMENTS

public static Pattern compile(String regex) {
    return new Pattern(regex, 0);
}
public static Pattern compile(String regex, int flags) {
    return new Pattern(regex, flags);
}

2.1.1 实例

Pattern p=Pattern.compile("\\d+"); 
Matcher m=p.matcher("22bb23"); 
m.matches();//返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功. 
Matcher m2=p.matcher("2223"); 
m2.matches();//返回true,因为\d+匹配到了整个字符串 

m=p.matcher("22bb23"); 
m.find();//返回true 
m2=p.matcher("aa2223"); 
m2.find();//返回true 
Matcher m3=p.matcher("aa2223bb"); 
m3.find();//返回true 
Matcher m4=p.matcher("aabb"); 
m4.find();//返回false 

m=p.matcher("22bb23"); 
m.lookingAt();//返回true,因为\d+匹配到了前面的22 
m2=p.matcher("aa2223"); 
m2.lookingAt();//返回false,因为\d+不能匹配前面的aa 

m=p.matcher("aaa2223bb"); 
m.find();//匹配2223 
m.start();//返回3 
m.end();//返回7,返回的是2223后的索引号 
m.group();//返回2223 
Pattern p=Pattern.compile("([a-z]+)(\\d+)"); 
Matcher m=p.matcher("aaa2223bb"); 
m.find();   //匹配aaa2223 
m.groupCount();   //返回2,因为有2组 
m.start(1);   //返回0 返回第一组匹配到的子字符串在字符串中的索引号 
m.start(2);   //返回3 
m.end(1);   //返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置. 
m.end(2);   //返回7 
m.group(1);   //返回aaa,返回第一组匹配到的子字符串 
m.group(2);   //返回2223,返回第二组匹配到的子字符串 

2.2 组和捕获

捕获组可以通过从左到右计算其开括号来编号。

在表达式 ((A)(B©)) 中,存在四个组:

  • ABC
  • A
  • BC
  • C

组零始终代表整个表达式。

2.3 int flags()方法

返回当前Pattern的匹配flag参数。

Pattern p = Pattern.compile("a+", Pattern.CASE_INSENSITIVE);
System.out.println(p.flags());// 2

2.4 String pattern() 方法

返回该Patter对象所编译的正则表达式。

Pattern p = Pattern.compile("\\d+");
System.out.println(p.toString());// 输出\d+
System.out.println(p.pattern());// 输出\d+

2.5 split(CharSequence input)

  • input 要拆分的字符序列。

  • return 根据围绕此模式的匹配来拆分输入后所计算的字符串数组。

此方法将目标字符串按照Pattern里所包含的正则表达式为模进行分割,它的工作方式类似于使用给定的输入序列和限制参数零调用两个参数的方法。因此,得到的数组中不包括尾部空字符串。

Pattern p=Pattern.compile("\\d+"); 
String[] str=p.split("我的QQ是:456456我的电话是:0532214我的邮箱是:aaa@aaa.com"); 

运行结果:str[0]="我的QQ是:" str[1]="我的电话是:" str[2]="我的邮箱是:aaa@aaa.com"

2.6 split(CharSequence input, int limit)

  • input 要拆分的字符序列。
  • limit 结果阈值,如上文中所述。
  • return 根据围绕此模式的匹配来拆分输入后所计算的字符串数组。

limit参数控制应用模式的次数,从而影响结果数组的长度。

  1. 如果 n 大于零,则模式至多应用 n- 1 次,数组长度不大于 n,且数组的最后条目将包含除最后匹配定界符之外的所有输入。
  2. 如果 n 非正,那么将应用模式的次数不受限制,并且数组可以为任意长度。
  3. 如果 n 为零,那么应用模式的次数不受限制,数组可以为任意长度,并且将丢弃尾部空字符串。

此方法返回的数组包含输入序列的子字符串,由匹配此模式的另一子序列或输入序列的结尾终止。数组中子字符串的顺序与其在输入中出现的顺序相同。如果此模式与输入的任何子序列都不匹配,那么得到的数组仅包含一个元素,即字符串形式的输入序列。

Pattern p = Pattern.compile("[/]+"); 
string[] result = p.split("Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。/名词:凯文。", 2); 

执行结果:

Kevin has seen《LEON》seveal times,because it is a good film. 
凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。/名词:凯文。

实战

public static void main(String[] args) {
    String[] arr = null;
    CharSequence input = "boo:and:foo";
    Pattern p = Pattern.compile("o");
    arr = p.split(input, -2);
    System.out.println(printArr(arr)); // {"b","",":and:f","",""},共有5个元素
    arr = p.split(input, 2);
    System.out.println(printArr(arr)); // {"b","o:and:foo"},共有2个元素
    arr = p.split(input, 7);
    System.out.println(printArr(arr)); // {"b","",":and:f","",""},共有5个元素
    arr = p.split(input, 0);
    System.out.println(printArr(arr)); // {"b","",":and:f"},共有3个元素
}

// 打印String数组
public static String printArr(String[] arr) {
    int length = arr.length;
    StringBuffer sb = new StringBuffer();
    sb.append("{");
    for (int i = 0; i < length; i++) {
        sb.append("\"").append(arr[i]).append("\"");
        if (i != length - 1) sb.append(",");
    }
    sb.append("}").append(",共有" + length + "个元素");
    return sb.toString();
}
  • 当limit=-2时,应用模式的次数不受限制且数组可以为任意长度;推测模式应用4次,数组的长度为5,数组为{“b”,“”,“:and:f”,“”,“”}。
  • 当limit=2时,模式至多应用1次,数组的长度不大于 2,且第二个元素包含除最后的匹配定界符之外的所有输入;推测模式应用1次,数组的长度为2,数组为{“b”,“o:and:foo”}。
  • 当limit=7时,模式至多应用6次,数组的长度不大于 7;推测模式应用4次,数组的长度为5,数组为{“b”,“”,“:and:f”,“”,“”}。
  • 当limit=0时,应用模式的次数不受限制,数组可以为任意长度,并且将丢弃尾部空字符串;推测模式应用4次,数组的长度为3,数组为{“b”,“”,“:and:f”}。

2.7 matches(String regex,CharSequence input)

此方法是一个静态方法,用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串。

Pattern.matches("\\d+","2223");//返回true 
Pattern.matches("\\d+","2223aa");//返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到 
Pattern.matches("\\d+","22bb23");//返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到 

2.8 matcher(CharSequence input)

返回一个Matcher对象,Pattern类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将Pattern与Matcher一起合作.Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持。

Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例。

Pattern.matches(String regex,CharSequence input),它与下面这段代码等价 Pattern.compile(regex).matcher(input).matches() 。

Pattern p=Pattern.compile("\\d+"); 
Matcher m=p.matcher("22bb23"); 
m.pattern();//返回p 也就是返回该Matcher对象是由哪个Pattern对象的创建的 

(1)实例

Pattern p=Pattern.compile("([a-z]+)(\\d+)"); 
Matcher m=p.matcher("aaa2223bb"); 
m.find();   //匹配aaa2223 
m.groupCount();   //返回2,因为有2组 
m.start(1);   //返回0 返回第一组匹配到的子字符串在字符串中的索引号 
m.start(2);   //返回3 
m.end(1);   //返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置. 
m.end(2);   //返回7 
m.group(1);   //返回aaa,返回第一组匹配到的子字符串 
m.group(2);   //返回2223,返回第二组匹配到的子字符串

三、Matcher类详解

Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查,此类的实例用于多个并发线程是不安全的。

一个Matcher实例是被用来对目标字符串进行基于既有模式(也就是一个给定的Pattern所编译的正则表达式)进行匹配查找的,所有往Matcher的输入都是通过CharSequence接口提供的,这样做的目的在于可以支持对从多元化的数据源所提供的数据进行匹配工作。

3.1 获取Matcher实例

Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例。

Matcher(Pattern parent, CharSequence text) {
    this.parentPattern = parent;
    this.text = text;

    // Allocate state storage
    int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
    groups = new int[parentGroupCount * 2];
    locals = new int[parent.localCount];

    // Put fields into initial states
    reset();
}

实例

Pattern p=Pattern.compile("\\d+"); 
Matcher m=p.matcher("22bb23"); 
m.pattern();//返回p 也就是返回该Matcher对象是由哪个Pattern对象的创建的 

3.2

上次编辑于:
贡献者: 诗人都藏在水底,xuliang