正则表达式入门——java语言

前情引入

  1. 什么是正则表达式?
    正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本—— 来自百度的解释。
    说得简单一点,正则表达式就是一套模式,一套对应规则。就像我们使用的字符集一样,有对应关系。字符集是用来与我们使用的语言文字对应的,正则表达式则是用来与字符对应的。
    比如在ASCII码中,97对应的是小写字母"a",在正则表达式中,"\w" 就对应任何一个单词字符(数字、字母和下划线)
  2. 正则表达式有什么用?
    有了这套对应关系之后,我们就可以很方便的对字符串(或称文本)进行各种操作,检索、替换等等。正则表达式被广泛的应用在计算机的各个领域,主流的开发语言(java、c、python、c++、js……)和数以亿万记的软件中,都可以看到正则表达式的身影。

正则表达式和java语言本身没有关系

入门初体验

在java中,java.util.regex包下的类都是和正则相关的类,我们来简单的体验一下

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTest02
{
    public static void main(String[] args)
    {
        regularTest();
    }

    public static void regularTest()
    {
        Pattern pattern = Pattern.compile("\\w");//被编译后的表达式对象
        Matcher matcher = pattern.matcher("abc123##");//需要匹配的对象(就是需要操作的字符串)
        boolean isMatch = matcher.matches();//matches这个方法是尝试匹配整个字符串
        
        //下面这种写法和上面的那种写法效果是一样的,只是上面的写法,可以将表达式对象和需要匹配的对象复用
        boolean isMatch1 = Pattern.matches("\\w", "a");//也是尝试匹配整个字符串
        System.out.println(isMatch);
        System.out.println(isMatch1);
    }
}

13行:表示获取一个被编译之后的正则表达式对象
14行:表示需要匹配的对象,也就是要对那个字符串进行操作
16行:matches方法是尝试匹配整个目标字符串,如果匹配成功,则返回true,否则返回false

如果是初学者,可能对于上面的内容不是很理解,但是java这种面向对象的语言,有这个好处:我们不需要知道为什么要这么做,而只需要知道我们如果要完成一个功能,需要怎么做(对于初学者而言)
我们需要怎么做?读文档、查资料,看哪个类、哪个方法能帮我们完成功能。现在我已经告诉你了,你只需要像上面这么做,就能达到我们想要的目的,所以,你照着上面这么写就好了。
需要注意一点的是,在java中,反斜杠 “\” 具有特殊含义:转义,所以想要表示一个普通的反斜杠,一个反斜杠是不行的,要用两个反斜杠,前面那个反斜杠的作用就是转义,将后面这个反斜杠转义为一个普通的反斜杠。如果里是在理解不了的话,你就记死结论:java中两个反斜杠表示一个普通的反斜杠
顺便提一嘴,在正则表达式中,反斜杠一样具有特殊含义,所以要在java中使用正则表达式表示一个普通的反斜杠,需要用四个反斜杠表示一个普通的反斜杠,后面会讲到。

现在我们来推测一下程序的运行结果,前面我已经说了,"\w" 就对代表何一个单词字符(数字、字母和下划线),那么控制台应该打印true还是false呢?如果将14行中的 “abc123##” 换成 “abc” 呢?如果换成 “a” 或其他的单词字符呢?可以自己去测试一下,再来看结果分析。

前两个都是false,因为 “\w” 只代表一个单词字符,而前两个目标字符串里都不止一个字符。如果换成 “a” 或其它的单词字符,肯定就是true了。

正式认识

前面我们简单的使用了正则表达式,下面我们来系统的认识一下正则的语法规则。简单的正则表达式大致由两部分组成:匹配内容和匹配次数,复杂的还有其它一些东西。
上面我们说的,"\w" 代表任意一个单词字符,但是如果我们要代表两个单词字符呢?三个或者更多个呢?

三个常用的表示次数的符号:
  • ? :表示匹配前一个字符零次或一次
  • +:表示匹配前一个字符一次或多次
  • * :表示匹配前一个字符任意次(零次或多次)

代码测试,还是上面那几行代码

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTest02
{
    public static void main(String[] args)
    {
        regularTest();
    }

    public static void regularTest()
    {
        System.out.println(Pattern.matches("\\w?","zbc"));
        System.out.println(Pattern.matches("\\w+","zbc"));
        System.out.println(Pattern.matches("\\w*","zbc"));
		System.out.println(Pattern.matches("\\w?",""));
		System.out.println(Pattern.matches("\\w+",""));
		System.out.println(Pattern.matches("\\w*",""));
    }
}

上面程序的打印结果是什么呢?
13行:false,因为 “?” 只代表前一个字符零次或一次,它的前一个字符是什么? 是 “\w” ,表示任意一个单词字符。所以整体意思表示匹配任意一个单词字符零次或一次。但是下面的需要匹配的字符串对象中,却有三个字符,所以肯定不匹配。
14行:true,因为 “+” 代表匹配前一个字符一次或多次,前一个字符是任意一个单词字符,所以,只要不为空,多少个单词字符拼在一起都可以匹配成功,当然是true了。

后面的尝试自己分析一下,另外,自己还可以多试一些边,加深影响。注意考虑三个表示次数的区别去改

其它表示次数的形式
  • {n,m}:匹配前一个字符n次到m次
  • {n,}:匹配前一个字符n次到无限次
  • {n}:匹配前一个字符n次

这三个,可以明确的表示一个次数边界,感觉上会用得多一些。但实际上用得并不多(就我个人经验而言),因为在对一些文本或字符串进行操作的时候,我们往往是不清楚个数的。
我就不测试了,大家感兴趣的话可以去测试一下,将之前的 “?”、"+"、"*" 改成上面的就可以了。

了解了表示匹配次数的语法之后,我们来认识一下表示匹配其它内容的规则

常用的匹配内容规则
  • \w:表示匹配任意一个单词字符(单词、字符和下划线)
  • \W:表示匹配任意一个单词字符
  • \d:表示匹配任意一个十进制数字
  • \D:表示匹配任意一个十进制数字
  • \s:表示匹配任意一个空白字符(包括空格、制表符、换页符等等)
  • \S:表示匹配任意一个空白字符,也就是可见字符
  • . : (英文状态下的点.) 表示匹配一个除换行符之外的其它任意字符

这三对都是相对应的,记住一个,另一个也就知道了。当然,还有很多表示表示匹配其它的规则,大家先熟悉和掌握这些基本的、常用的,再往了解其它的,有个循序渐进的过程。

代码测试,知道了这些匹配内容的规则,再结合前面的匹配次数,我们就可以换更多的花样来测试了。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTest02
{
    public static void main(String[] args)
    {
        regularTest();
    }

    public static void regularTest()
    {
        System.out.println(Pattern.matches("\\d?","zbc"));
        System.out.println(Pattern.matches("\\d+","zbc"));
        System.out.println(Pattern.matches("\\D*","zbc"));
		System.out.println(Pattern.matches("\\s?",""));
		System.out.println(Pattern.matches("\\S+",""));
		System.out.println(Pattern.matches("\\S*",""));
		System.out.println(Pattern.matches(".","a"));
		System.out.println(Pattern.matches(".?","abc"));
		System.out.println(Pattern.matches(".+","abc"));
    }
}

结果我就不带领大家分析了,自己慢慢分析,应该是可以得出结果的。如果分析错了,结合控制台的打印,应该也能很快反应过来的。

下面,我们来认识一下正则表达式中另一类神奇的存在:元字符。其实我们在之前已经用过了, 只是我们不知道它是元字符。例如:" "、 “{}”、 “*” ……这些都是元字符。什么?我之前用得好好的,现在你告诉我它是元字符,我不能接受!!

元字符是什么意思呢?就是具有特殊含义的字符。说到特殊含义,那什么是没有特殊含义的字符呢?说到现在,我们一直都在说匹配任意xxxx,那我就想匹配一个 “w” ,一个 “s” 呢?这咋办。
其实很简单

		System.out.println(Pattern.matches("w","w"));
		System.out.println(Pattern.matches("w?","www"));
		System.out.println(Pattern.matches("s+","sss"));
		System.out.println(Pattern.matches("s*","ass"));

如果是普通字符,单写一个,就代表匹配一个该字符。

现在我们再回头来看元字符,是不是具有特殊含义?是的吧,

常用的元字符
  • . : 表示匹配一个除换行符之外的其它任意字符
  • ? :表示匹配前一个字符零次或一次
  • + :表示匹配前一个字符一次或多次
  • * :表示匹配前一个字符任意次(零次或多次)
  • \:加在元字符前面可以消除其特殊功能,加在一些普通字符前面可以使其具有特殊含义
  • {}:成对出现,表示匹配中括号中指定的次数
  • []:成对出现,表示一个字符集,或者是一个范围,匹配其中一个。有些元字符在其中没有特殊含义
  • ():表示一个整体,也可以表示分组(子组),结合匹配对象进行使用
  • ^:脱字符,表示除xxx之外。还表示从字符串的头开始匹配
  • |:对两个正则表达式进行或操作,这个符号优先级别很低

有一些是老熟人了吧,我就不说了哦。

1. \

“\” 好像挺有意思的,加在元字符前面可以消除其特殊功能,加在一些普通字符前面可以使其具有特殊含义,是什么意思呢?我们都知道了,"+" 表示匹配前一个字符一次或多次,那我就想匹配 “+” 本身怎么办?

		//这样写肯定是不行的
		System.out.println(Pattern.matches("+","++"));

那怎么办呢?这时候就要用到 “\” 了,让 “\” 把 “+” 这个元字符的特殊含义给消除了,所以就这么写:

		//注意要用双反斜杠,前面已经解释过了
		System.out.println(Pattern.matches("\\+","++"));
		System.out.println(Pattern.matches("\\+?","++"));

要匹配其它的元字符也是类似,但是有个问题是:我就想匹配一个反斜杠,这咋办?
其实思路还是一样的,反斜杠不是具有特殊含义吗?那我就再用一个反斜杠,来消除它的特殊含义,只不过在java中,反斜杠也具有特殊含义,所以我们得用两个反斜杠表示字符串中一个普通的反斜杠,所以我们得用四个反斜杠来表示正则表达式中一个普通的反斜杠,每两个反斜杠,表示正则表达式中一个具有特殊含义的反斜杠,然后再用反斜杠消除特殊后面一个反斜杠的特殊含义。

		System.out.println(Pattern.matches("\\\\","\\"));
		System.out.println(Pattern.matches("\\\\?","\\\\"));
		System.out.println(Pattern.matches("\\\\*","\\\\\\"));

“\” 的另外一个作用就是,将一些普通字符变得有特殊含义,其实我们早已经用过了,就是在匹配内容那里,“w”、“W”、“s”、“S”、“d”、“D”……这些原本都是普通的字符,只代表匹配它本身,但是在它前面加一个反斜杠之后,就赋予了它特殊的含义。

2. []

“[]” 表示一个字符集,匹配其中一个。例如:"[123]" 表示匹配 “1” 或 “2” 或 “3”。

		System.out.println(Pattern.matches("[123]","3"));

当连字符( “-” )在字符集中出现在两个有顺序的字符之间时,表示匹配这个范围之内的任意一个,比如:"[1-3]" 的效果就和 “[123]” 一样,都表示匹配 “1” 或 “2” 或 “3”。"[a-c]"就表示匹配 “a” 或 “b” 或 “c”

		System.out.println(Pattern.matches("[1-3]","3"));
		System.out.println(Pattern.matches("[a-c]","a"));
		System.out.println(Pattern.matches("[a-c]","b"));
		System.out.println(Pattern.matches("[a-c]","c"));
注意:当连字符不是出现在两个 \color{red}{具有顺序关系} 的字符之间时,就表示匹配连字符本身。

比如:"[-123]"、"[123-]" 和 “[1-3-]” 都表示匹配 “-” 或 “1” 或 “2” 或"3"

		System.out.println(Pattern.matches("[-123]","-"));//true
		System.out.println(Pattern.matches("[123-]","-"));//true
		System.out.println(Pattern.matches("[1-3-]","-"));//true

在比如"[1-z]"就表示匹配 “1” 或 “-” 或 “z”,如果是 “[3-1]” 这种逆序的写法,在java中会报错,编译不通过,不知道其它情况下是怎样的。
还有就是有些元字符在 “[ ]” 中没有特殊含义!!

3. ()

“()” 表示一个整体,比如说,我要匹配 “110” 一次或多次,就可以用 “()” 将 “110” 表示为一个整体,然后后面再规定次数。

        System.out.println(Pattern.matches("(110)+","110"));
        System.out.println(Pattern.matches("(110)+","110110"));
        System.out.println(Pattern.matches("(110)+","110011"));

关于表示子组后面再说。

4. ^

“^” 在字符集 “[]” 中,如果出现在第一个,是脱字符,表示除去什么什么。
比如 “[^123]” 表示匹配任意一个 “1”、“2”、“3” 之外的字符。 " [^a-d]" 表示匹配任意一个除"a"、“b”、“c”、“d” 之外的字符。

        System.out.println(Pattern.matches("[^123]","3"));
        System.out.println(Pattern.matches("[^123]","5"));
        System.out.println(Pattern.matches("[^a-d]","d"));
        System.out.println(Pattern.matches("[^a-d]","z"));

如果 “^” 在 “[]” 中,不是出现在第一个的,就表示一个普通的 “^” 字符。比如 “[12^3]” 就表示匹配 “1” 或 “2” 或 “^” 或 “3”。

		System.out.println(Pattern.matches("[12^3]","^"));

如果"^“不是出现在字符集”[ ]"中,是表示从头开始匹配的意思,但是我不知道用java怎么演示。

5. |

“|” 表示对两个正则表达式进行或操作,这个符号优先级别很低,比如:“ab|cd” 不是表示匹配 “abd” 或 “acd” ,而是表示匹配 “ab” 或 "cd"

        System.out.println(Pattern.matches("ab|cd","abd"));
        System.out.println(Pattern.matches("ab|cd","acd"));
        System.out.println(Pattern.matches("ab|cd","ab"));
        System.out.println(Pattern.matches("ab|cd","cd"));

暂时就说这么多,最基本的内容。

热身练习

  1. 匹配非负整数
  2. 匹配正整数
  3. 匹配整数

习题是我从别处搬来的,想练习可以去这里:https://blog.csdn.net/qian_youyou/article/details/79121916
他给的答案中,每个表达式最前面都是"^",最后面都是"$",大家如果不知道的这个东西话,可以暂时将其忽略。

如果哪里写得有问题,还请指出。

猜你喜欢

转载自blog.csdn.net/ql_7256/article/details/107497318