在Java中使用正则表达式

我对正则表达式的理解

正则表达式Regex(Regular Expression),是一种通过定义由特定字符组成的表达式来对字符串进行匹配、查找、替换和切割的字符串操作工具。

正则表达式中特定的一些字符

表达式 匹配内容
字符
x 字符 x
\\ 反斜线字符
\0n 带有八进制值 0 的字符 n (0 <= n <= 7)
\0nn 带有八进制值 0 的字符 nn (0 <= n <= 7)
\0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7)
\xhh 带有十六进制值 0x 的字符 hh
\uhhhh 带有十六进制值 0x 的字符 hhhh
\t 制表符 ('\u0009')
\n 新行(换行)符 ('\u000A')
\r 回车符 ('\u000D')
\f 换页符 ('\u000C')
\a 报警 (bell) 符 ('\u0007')
\e 转义符 ('\u001B')
\cx 对应于 x 的控制符
字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)
预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
Greedy数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次

正则表达式在Java中的表现形式

public final class Pattern
extends Object
implements Serializable

Pattern正则表达式的编译表示形式。

指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。

因此,典型的调用顺序是

 Pattern p = Pattern.compile("a*b");
 Matcher m = p.matcher("aaaaab");
 boolean b = m.matches();

在仅使用一次正则表达式时,可以方便地通过此类定义 matches 方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句

 boolean b = Pattern.matches("a*b", "aaaaab");

等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。

使用正则表达式对字符串进行匹配

使用正则表达式对字符串进行匹配有三种方式:
1)使用字符串的对象的matches()方法
2)使用Matcher的对象的matches()方法
3)使用Pattern类的matches()方法
匹配的特点:
1)根据正则表达式的规则对整个字符串进行匹配
2)匹配结果返回对应的布尔值
使用方法:
请参考下面匹配字符串是否为电话号码的实例

public class MatchTest {

    public static void main(String[] args) {
        System.out.println("一,使用字符串的对象的matches()方法");
        matchDemo_1("13805646681");
        matchDemo_1("03805646681");
        matchDemo_1("13805Jia681");
        matchDemo_1("138056");

        System.out.println("二,使用Matcher的对象的matches()方法");
        matchDemo_2("13805646681");
        matchDemo_2("03805646681");
        matchDemo_2("13805Jia681");
        matchDemo_2("138056");

        System.out.println("三,使用Pattern类的matches()方法");
        matchDemo_3("13805646681");
        matchDemo_3("03805646681");
        matchDemo_3("13805Jia681");
        matchDemo_3("138056");
    }

    /**
     * 匹配字符串是否为电话号码
     * 匹配规则:
     * 1.电话号码以数字1开头
     * 2.电话号码长度为11
     * 3.电话号码为全数字
     * 对应的正则表达式:
     * 1)"1\\d{10}"
     * 解读:
     *      正则表达是第一个数是1,所以它只能匹配以1开头的字符串;
     *      \\d,因为Java中反斜杠会把它后面的字符进行转移,所以\\d就是正则表达式中预定义字符类中的\d(== [0-9]),即能够匹配数字0-9中的任意一个;
     *      {10},代表前面的\d恰好出现10次,这样加上第一个数1共有11个数字,所以它只能匹配长度为11且全部为数字的字符串。
     * 2)"1[0-9]{10}"
     * 解读:
     *      \d == [0-9]
     */
    public static void matchDemo_1(String str) {
        String regex = "1\\d{10}";
        boolean isMatched = str.matches(regex);
        System.out.println(str + (isMatched ? "是电话号码" : "不是电话号码"));
    }

    public static void matchDemo_2(String str) {
        String regex = "1[0-9]{10}";
        // 1,将正则表达式编译成Pattern对象
        Pattern p = Pattern.compile(regex);
        // 2, 与字符串进行关联,生成Matcher对象
        Matcher m = p.matcher(str);
        // 3,对字符串进行操作
        boolean isMatched = m.matches();
        System.out.println(str + (isMatched ? "是电话号码" : "不是电话号码"));
    }

    public static void matchDemo_3(String str) {
        String regex = "1\\d{10}";
        boolean isMatched = Pattern.matches(regex, str);
        System.out.println(str + (isMatched ? "是电话号码" : "不是电话号码"));
    }
}

使用正则表达式对字符串进行替换

正则表达式既可以替换字符串中所有匹配到的字符,也可以只替换第一次匹配到的字符,对应的两个方法是replaceALL和replaceFirst,同样在String对象和Matcher对象中都包含这两个方法。
替换的特点:
1)可以将正则表达式匹配到的字符(串)替换为你指定的字符(串)
2)替换结果生成新的字符串
使用方法:
请参考下面叠词替换的实例

public class StackedWordsReplace {
    public static void main(String[] args) {
        stackedWordsReplaceDemo_1();
        stackedWordsReplaceDemo_2();
    }

    public static void stackedWordsReplaceDemo_1() {
        // 将下面的字符串转换成"我要学习编程。"
        String str = "我我我我我要要学学学编编编编程程程程。";

        // 正则表达式中通过()创建一个捕获组
        // 捕获组默认从1开始进行编号,可以通过从左到右计算开括号(左括号)个数和顺序进行排序
        // 例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:((A)(B(C)))、(A)、(B(C))、(C)
        // 正则表达式中的\n和$n分别在匹配环节和替换环节中引用捕获组捕获的内容,n代表数字1、2、3...
        // "(.)\1+"解读:
        // .可以匹配任意字符,\\1即\1引用(.)的捕获内容,+前面的字符出现一次或多次。
        // 比如,当.匹配的到我的时候\1就是我,所以这个表达式能够匹配到无数个我连续组成但最低不少两个我的字符串。
        // $1为替换的类容,因为$1引用(.)的捕获内容,所以当.匹配到什么就替换为什么
        str = str.replaceAll("(.)\\1+", "$1");

        System.out.println(str);
    }

    public static void stackedWordsReplaceDemo_2() {
        String str = "我我我我我要要学学学编编编编程程程程。";
        String regex = "(.)\\1+";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);
        str = matcher.replaceAll("$1");
        System.out.println(str);
    }
}

使用正则表达式对字符串进行切割

使用正则表达式对字符串进行切割有两种方式:
1)使用String对象的split方法
2)使用Pattern对象的split方法
切割的特点:
1)可以将正则表达式匹配到的字符(串)作为分隔符来对字符串进行切割
2)切割结果为子串组成的字符串数组
使用方法:
请参考下面的切割实例

public class SplitTest {
    public static void main(String[] args) {
        splitDemo_1();
        splitDemo_2();
    }

    public static void splitDemo_1() {
        // 将下面的字符串以.为分隔符进行切割
        // 由于在正则表达式中.为预定义字符,所以需要用\进行转义,
        // 在Java中\也是转义字符,所以仍需要进行转义
        String str = "我.爱.中.国";
        String regex = "\\.";
        String[] strings = str.split(regex);
        for (String s : strings) {
            System.out.println(s);
        }
    }

    public static void splitDemo_2() {
        String str = "我.爱.中.国";
        String regex = "\\.";
        Pattern pattern = Pattern.compile(regex);
        String[] strings = pattern.split(str);
        for (String s : strings) {
            System.out.println(s);
        }
    }
}

综合案例演示

下面将演示综合使用和切割来对IP地址进行排序

public class IPSort {

    public static void main(String[] args) {
        ipSort();
    }

    /**
     * 将IP地址进行排序
     */
    public static void ipSort() {
        String ip = "127.0.0.1 192.168.0.1 114.114.114.114 8.8.8.8 10.2.33.134 255.255.255.255";

        // 1.将IP地址全部替换为xxx.xxx.xxx.xxx样式
        ip = ip.replaceAll("\\w{1,3}", "00$0"); // 将IP地址每一段都添加00
        System.out.println(ip);
        ip = ip.replaceAll("0*(\\w{3})", "$1"); // 去除每一段多余的0,是每一段只保留三位数字
        System.out.println(ip);

        // 2.对字符串进行排序
        String[] arr = ip.split(" ");
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr);
        for (String s : arr) {
            // 去除添加的0
            s = s.replaceAll("0*(\\w+)", "$1");
            System.out.println(s);
        }
    }
}

使用正则表达式对字符串进行查找

对字符串进行查找,主要用的是Matcher对象的以下方法:
1)find()尝试查找与该模式匹配的输入序列的下一个子序列
2)group()返回上一次匹配操作所匹配的输入子序列
3)reset(CharSequence input)将该模式应用到新的输入序列
查找的特点:
1)可以将正则表达式匹配到的字符(串)逐个找出来
2)查找结果为一个个匹配的子串
3)必须逐个查找,直接调用group()方法无法得到结果
使用方法:
请参考下面的爬去网页中的电子邮箱实例

public class FindEmail {

    public static void main(String[] args) {
        findEmail();
    }

    public static void findEmail() {
        // 简单邮箱匹配规则
        String reg = "\\w+@\\w+(\\.\\w+)+";
        // 1.将正则表达式编译成Pattern对象
        Pattern p = Pattern.compile(reg);
        Matcher m = null;
        try {
            URL url = new URL("https://www.douban.com/group/topic/70867518/");
            URLConnection connection = url.openConnection();
            BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line = null;
            while ((line = br.readLine()) != null) {
                // 2.将字符串和Pattern对象进行关联,生成Matcher对象
                if (m == null) {
                    m = p.matcher(line);
                } else {
                    m.reset(line);
                }
                // 3.通过Matcher对象对字符串进行操作
                while (m.find()) {
                    System.out.println(m.group());
                }
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容