Java正则表达式学习

简介: 在日常的开发中经常需要使用到正则表达式,它是涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在比如验证一个邮件是否是合法的邮件地址,验证身份证是否合法等等都可以使用正则表达式。

01 什么是正则表达式


在日常的开发中经常需要使用到正则表达式,它是涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在比如验证一个邮件是否是合法的邮件地址,验证身份证是否合法等等都可以使用正则表达式。


正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。


比如验证一个邮件是否合法:


public class EmailConfirm {
    public static void main(String[] args) {
        // 要验证的字符串
        String str = "testmail@163.com";
        // 邮箱验证规则
        String regEx = "[a-zA-Z_]{1,}[0-9]{0,}@(([a-zA-z0-9]-*){1,}\\.){1,3}[a-zA-z\\-]{1,}";
        // 编译正则表达式
        Pattern pattern = Pattern.compile(regEx);
        Matcher matcher = pattern.matcher(str);
        // 字符串是否与正则表达式相匹配
        boolean rs = matcher.matches();
        System.out.println(rs);
    }
}
复制代码


结果如下:


0bcb1a2db8024edd84130b230e3b68f6~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

如果将验证的邮件字符串改为以下:


String str = "testmailz163.com";
复制代码


则输出结果:

9451983e24e8497a950dce6bf9a2812d~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。


java.util.regex 包主要包括以下三个类:


import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
复制代码


Pattern 类:


pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。


Matcher 类:


Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。


PatternSyntaxException类:


PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。


02 正则表达式语法


在其他语言中,\\ 表示正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。而在 Java 中,\\  表示要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。


所以,在其他的语言中一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\。


8135da36d1a245659708eb705320cd73~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


关于正则表达式的一些语法规则整理如下:


元字符 描述
\ 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如, n匹配字符 n。\n 匹配换行符。序列 \\ 匹配 \ ,\( 匹配 (。
^ 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。
* 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。
+ 一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。
? 零次或一次匹配前面的字符或子表达式。例如,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。
{n} n 是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。
{n,} n 是非负整数。至少匹配 n 次。例如,"o{2,}"不匹配"Bob"中的"o",而匹配"foooood"中的所有 o。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"。
{n,m} m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。
? 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?"只匹配单个"o",而"o+"匹配所有"o"。
. 匹配除"\r\n"之外的任何单个字符。若要匹配包括"\r\n"在内的任意字符,请使用诸如"[\s\S]"之类的模式。
(pattern) 匹配 pattern 并捕获该匹配的子表达式。可以使用 0…0…09 属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用"("或者")"。
(?:pattern) 匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (
(?=pattern) 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95
(?!pattern) 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95
x y
[xyz] 字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。
[^xyz] 反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。
[a-z] 字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。
[^a-z] 反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。
\b 匹配一个字边界,即字与空格间的位置。例如,"er\b"匹配"never"中的"er",但不匹配"verb"中的"er"。
\B 非字边界匹配。"er\B"匹配"verb"中的"er",但不匹配"never"中的"er"。
\cx 匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是"c"字符本身。
\d 数字字符匹配。等效于 [0-9]。
\D 非数字字符匹配。等效于 [^0-9]。
\f 换页符匹配。等效于 \x0c 和 \cL。
\n 换行符匹配。等效于 \x0a 和 \cJ。
\r 匹配一个回车符。等效于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。
\S 匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。
\t 制表符匹配。与 \x09 和 \cI 等效。
\v 垂直制表符匹配。与 \x0b 和 \cK 等效。
\w 匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。
\W 与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。
\xn 匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,"\x41"匹配"A"。"\x041"与"\x04"&"1"等效。允许在正则表达式中使用 ASCII 代码。
*num* 匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,"(.)\1"匹配两个连续的相同字符。
*n* 标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码。
*nm* 标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 n 和 m 是八进制数字 (0-7)。
\nml 当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml。
\un 匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (©)。


03 java中正则表达式相关方法


前面说到java的工具包中提供了正则表达式相关的类,主要有三个类:Matcher、Pattern、PatternSyntaxException,每个类都提供了对应的方法。


Matcher 类提供的方法:


1、索引方法


索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:


//返回以前匹配的初始索引
public int start()
//返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引
public int start(int group) 
//返回最后匹配字符之后的偏移量。
public int end() 
//返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。
public int end(int group)
复制代码


源码如下:


public int start() {
        if (first < 0)
            throw new IllegalStateException("No match available");
        return first;
    }
    public int start(int group) {
        if (first < 0)
            throw new IllegalStateException("No match available");
        if (group < 0 || group > groupCount())
            throw new IndexOutOfBoundsException("No group " + group);
        return groups[group * 2];
    }
    public int start(String name) {
        return groups[getMatchedGroupIndex(name) * 2];
    }
    public int end() {
        if (first < 0)
            throw new IllegalStateException("No match available");
        return last;
    }
    public int end(int group) {
        if (first < 0)
            throw new IllegalStateException("No match available");
        if (group < 0 || group > groupCount())
            throw new IndexOutOfBoundsException("No group " + group);
        return groups[group * 2 + 1];
    }
    public int end(String name) {
        return groups[getMatchedGroupIndex(name) * 2 + 1];
    }
    public String group() {
        return group(0);
    }
    public String group(int group) {
        if (first < 0)
            throw new IllegalStateException("No match found");
        if (group < 0 || group > groupCount())
            throw new IndexOutOfBoundsException("No group " + group);
        if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
            return null;
        return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
    }
复制代码


栗子:


public class RegexDemo1 {
    //\b:匹配一个字边界,即字与空格间的位置。
    private static final String REGEX = "\\bHello\\b";
    private static final String INPUT = "Hello World Hello China";
    public static void main( String[] args ){
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT);
        // 获取 matcher 对象
        int count = 0;
        while(m.find()) {
            count++;
            System.out.println("Match number "+count);
            System.out.println("start(): "+m.start());
            System.out.println("end(): "+m.end());
        }
    }
}
复制代码


3b5f7f7883f64532b3ce022fe5ada6b1~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


2、查找方法:


查找方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式


//尝试将从区域开头开始的输入序列与该模式匹配。
public boolean lookingAt()  
//尝试查找与该模式匹配的输入序列的下一个子序列。
public boolean find() 
// 重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
public boolean find(int start)
//尝试将整个区域与模式匹配。
public boolean matches()
复制代码


源码如下:


public boolean matches() {
        return match(from, ENDANCHOR);
}
public boolean find() {
        int nextSearchIndex = last;
        if (nextSearchIndex == first)
            nextSearchIndex++;
        // If next search starts before region, start it at region
        if (nextSearchIndex < from)
            nextSearchIndex = from;
        // If next search starts beyond region then it fails
        if (nextSearchIndex > to) {
            for (int i = 0; i < groups.length; i++)
                groups[i] = -1;
            return false;
        }
        return search(nextSearchIndex);
}
public boolean find(int start) {
        int limit = getTextLength();
        if ((start < 0) || (start > limit))
            throw new IndexOutOfBoundsException("Illegal start index");
        reset();
        return search(start);
}  
public boolean lookingAt() {
        return match(from, NOANCHOR);
}
复制代码


栗子:


public class RegexDemo2 {
        //目标字段
        private static final String REGEX = "he";
        private static final String INPUT1 = "hello";
        private static final String INPUT2 = "yousayhello";
        private static Pattern pattern;
        private static Matcher matcher;
        private static Matcher matcher2;
        public static void main(String[] args) {
            pattern = Pattern.compile(REGEX);
            matcher = pattern.matcher(INPUT1);
            matcher2 = pattern.matcher(INPUT2);
            System.out.println("Current REGEX is: " + REGEX);
            System.out.println("Current INPUT is: " + INPUT1);
            System.out.println("Current INPUT2 is: " + INPUT2);
            System.out.println("lookingAt(): " + matcher.lookingAt());
            System.out.println("matches(): " + matcher.matches());
            System.out.println("lookingAt(): " + matcher2.lookingAt());
        }
    }
复制代码


5e6edc38662e4eee91a0cf7900e97c0e~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


3、替换方法:


替换方法是替换输入字符串里文本的方法:


//实现非终端添加和替换步骤。
public Matcher appendReplacement(StringBuffer sb, String replacement) 
// 实现终端添加和替换步骤。
public StringBuffer appendTail(StringBuffer sb)
//替换模式与给定替换字符串相匹配的输入序列的每个子序列。
public String replaceAll(String replacement)  
//  替换模式与给定替换字符串匹配的输入序列的第一个子序列。
public String replaceFirst(String replacement)
// 返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。
public static String quoteReplacement(String s)
复制代码


源码如下:


public Matcher appendReplacement(StringBuffer sb, String replacement) {
        // If no match, return error
        if (first < 0)
            throw new IllegalStateException("No match available");
        // Process substitution string to replace group references with groups
        int cursor = 0;
        StringBuilder result = new StringBuilder();
        while (cursor < replacement.length()) {
            char nextChar = replacement.charAt(cursor);
            if (nextChar == '\\') {
                cursor++;
                if (cursor == replacement.length())
                    throw new IllegalArgumentException(
                        "character to be escaped is missing");
                nextChar = replacement.charAt(cursor);
                result.append(nextChar);
                cursor++;
            } else if (nextChar == '$') {
                // Skip past $
                cursor++;
                // Throw IAE if this "$" is the last character in replacement
                if (cursor == replacement.length())
                   throw new IllegalArgumentException(
                        "Illegal group reference: group index is missing");
                nextChar = replacement.charAt(cursor);
                int refNum = -1;
                if (nextChar == '{') {
                    cursor++;
                    StringBuilder gsb = new StringBuilder();
                    while (cursor < replacement.length()) {
                        nextChar = replacement.charAt(cursor);
                        if (ASCII.isLower(nextChar) ||
                            ASCII.isUpper(nextChar) ||
                            ASCII.isDigit(nextChar)) {
                            gsb.append(nextChar);
                            cursor++;
                        } else {
                            break;
                        }
                    }
                    if (gsb.length() == 0)
                        throw new IllegalArgumentException(
                            "named capturing group has 0 length name");
                    if (nextChar != '}')
                        throw new IllegalArgumentException(
                            "named capturing group is missing trailing '}'");
                    String gname = gsb.toString();
                    if (ASCII.isDigit(gname.charAt(0)))
                        throw new IllegalArgumentException(
                            "capturing group name {" + gname +
                            "} starts with digit character");
                    if (!parentPattern.namedGroups().containsKey(gname))
                        throw new IllegalArgumentException(
                            "No group with name {" + gname + "}");
                    refNum = parentPattern.namedGroups().get(gname);
                    cursor++;
                } else {
                    // The first number is always a group
                    refNum = (int)nextChar - '0';
                    if ((refNum < 0)||(refNum > 9))
                        throw new IllegalArgumentException(
                            "Illegal group reference");
                    cursor++;
                    // Capture the largest legal group string
                    boolean done = false;
                    while (!done) {
                        if (cursor >= replacement.length()) {
                            break;
                        }
                        int nextDigit = replacement.charAt(cursor) - '0';
                        if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
                            break;
                        }
                        int newRefNum = (refNum * 10) + nextDigit;
                        if (groupCount() < newRefNum) {
                            done = true;
                        } else {
                            refNum = newRefNum;
                            cursor++;
                        }
                    }
                }
                // Append group
                if (start(refNum) != -1 && end(refNum) != -1)
                    result.append(text, start(refNum), end(refNum));
            } else {
                result.append(nextChar);
                cursor++;
            }
        }
        // Append the intervening text
        sb.append(text, lastAppendPosition, first);
        // Append the match substitution
        sb.append(result);
        lastAppendPosition = last;
        return this;
    }
    public StringBuffer appendTail(StringBuffer sb) {
        sb.append(text, lastAppendPosition, getTextLength());
        return sb;
    }
    public String replaceAll(String replacement) {
        reset();
        boolean result = find();
        if (result) {
            StringBuffer sb = new StringBuffer();
            do {
                appendReplacement(sb, replacement);
                result = find();
            } while (result);
            appendTail(sb);
            return sb.toString();
        }
        return text.toString();
    }
    public String replaceFirst(String replacement) {
        if (replacement == null)
            throw new NullPointerException("replacement");
        reset();
        if (!find())
            return text.toString();
        StringBuffer sb = new StringBuffer();
        appendReplacement(sb, replacement);
        appendTail(sb);
        return sb.toString();
    }
    public Matcher region(int start, int end) {
        if ((start < 0) || (start > getTextLength()))
            throw new IndexOutOfBoundsException("start");
        if ((end < 0) || (end > getTextLength()))
            throw new IndexOutOfBoundsException("end");
        if (start > end)
            throw new IndexOutOfBoundsException("start > end");
        reset();
        from = start;
        to = end;
        return this;
    }
复制代码


栗子:


public class RegexDemo3 {
    private static String REGEX = "xiaoming";
    private static String INPUT = "xiaoming says hello";
    private static String REPLACE = "jiangxia";
    public static void main(String[] args) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT);
        INPUT = m.replaceAll(REPLACE);
        System.out.println(INPUT);
    }
}
复制代码


8cf56be60238436d9ab9321c22f964ec~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


Pattern类提供的方法*:


Pattern是模式类,表示正则表达式的编译表示形式。Pattern类有final 修饰,可知他不能被子类继承。Pattern类提供了以下方法:


public Matcher matcher(CharSequence input)
public static boolean matches(String regex, CharSequence input)
public static Pattern compile(String regex, int flags)
复制代码


具体栗子可以参考前面。


PatternSyntaxException 类的方法


PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。


PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。


public String getDescription()
public int getIndex()  
public String getPattern()
public String getMessage()
复制代码


举个栗子:


public class RegexDemo4 {
    private static String REGEX = "[";
    private static String INPUT = "xiaoming says hello";
    private static String REPLACE = "jiangxia";
    public static void main(String[] args) {
        try{
            Pattern pattern = Pattern.compile(REGEX);
            // get a matcher object
            Matcher matcher = pattern.matcher(INPUT);
            INPUT = matcher.replaceAll(REPLACE);
        } catch(PatternSyntaxException e){
            System.out.println("PatternSyntaxException: ");
            System.out.println("Description: "+ e.getDescription());
            System.out.println("Index: "+ e.getIndex());
            System.out.println("Message: "+ e.getMessage());
            System.out.println("Pattern: "+ e.getPattern());
        }
    }
}
复制代码


7b6e5fc6386245a5a31aed59489627ad~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


04 总结


以上就是java中正则表达式的使用,在编程中正则表达式的使用还是挺多的,熟练的掌握正则表达式的使用,能够大大提高开发的效率。

目录
相关文章
|
7天前
|
消息中间件 前端开发 Java
java学习路径
【4月更文挑战第9天】java学习路径
15 1
|
21天前
|
Java
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
24 0
|
7天前
|
设计模式 前端开发 安全
Java是一种广泛使用的编程语言,其学习路径可以大致分为以下几个阶段
【4月更文挑战第9天】Java是一种广泛使用的编程语言,其学习路径可以大致分为以下几个阶段
11 1
|
7天前
|
XML 数据可视化 前端开发
java正则表达式
java正则表达式
|
4天前
|
Java
Java 14 强势登场:Switch 表达式的进化之路
Java 14 强势登场:Switch 表达式的进化之路
12 0
|
4天前
|
Java 存储
键值之道:深入学习Java中强大的HashMap(二)
键值之道:深入学习Java中强大的HashMap
8 0
键值之道:深入学习Java中强大的HashMap(二)
|
6天前
|
Java 开发者
Java中的Lambda表达式:简洁、灵活的编程利器
在现代软件开发中,编写简洁、高效的代码是至关重要的。Java中的Lambda表达式为开发者提供了一种简洁、灵活的编程方式,使得代码更具可读性和可维护性。本文将探讨Lambda表达式的基本概念、语法结构以及在实际项目中的应用,以帮助读者更好地理解和运用这一强大的编程工具。
5 0
|
6天前
|
JavaScript Java 测试技术
基于Java的网络类课程思政学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的网络类课程思政学习系统的设计与实现(源码+lw+部署文档+讲解等)
25 0
基于Java的网络类课程思政学习系统的设计与实现(源码+lw+部署文档+讲解等)
|
7天前
|
存储 Java API
java8新特性 lambda表达式、Stream、Optional
java8新特性 lambda表达式、Stream、Optional
|
22天前
|
Java API 开发者
Java中的Lambda表达式及其应用
本文将介绍Java中的Lambda表达式,探讨其在函数式编程中的作用和应用。通过对Lambda表达式的语法、特点以及实际应用场景的详细分析,读者将能够更好地理解并运用Lambda表达式,从而提高代码的简洁性和可读性。
16 1