正则表达式学习

生成正则图

正则表达式简单、强大,它可以极大地提高我们工作中的文本处理效率。现在,各大操作系统、编程语言、文本编辑器都已经支持正则表达式

正则其实就是一种描述文本内容组成规律的表示方式。正则表达式真正重要的是字符组、多选结构、量词等等这些概念

正则常常用来简化文本处理的逻辑。在 Linux 命令中,它也可以帮助我们轻松地查找或编辑文件的内容,甚至实现整个文件夹中所有文件的内容替换,比如 grep、
egrep、sed、awk、vim 等。另外,在各种文本编辑器中,比如 Atom,Sublime Text 或VS Code 等,在查找或替换的时候也会使用到它。

正则常见的三种功能:

1> 校验数据的有效性(如校验手机号 邮箱)、

2> 查找符合要求的文本内容(查找符合某规则的号码)

3> 对文本进行切割和替换(比如用连续的空白符切割)等操作。比如 Word、Excel 中查找替换
正则表达式的基本单元——元字符, 元字符是指具有特殊意义的专用字符,元字符是构成正则表达式的基本元件。正则就是由一系列的元字符组成的
例中提到的 \d 和 {11}。

元字符的分类与记忆技巧

正则表达式中有很多的“元字符”,比如刚刚提到的 \d,它在正则中不代表 \(反斜杠)
加字母 d,而是代表任意数字,这种表示特殊含义的字符表示,就是元字符。

元字符大致分成这几类:表示单个特殊字符的,表示空白符的,表示某个范
围的,表示次数的量词,另外还有表示断言的,我们可以把它理解成边界限定

1. 特殊单字符
比如英文的点 . 表示换行以外的任意单个字符,\d 表示任意单个数字,\w 表示任意单个数字或字母或下划线,\s 表示任意单个
空白符。另外,还有与之对应的三个 \D、\W 和 \S,分别表示着和原来相反的意思。
https://regex101.com/r/PnzZ4k/1  \d的使用
https://regex101.com/r/PnzZ4k/2  \w的使用

1> \d 正则匹配所有的数字 
12345
67890 
abcde
fghij

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

public class Main {
  public static void main(String[] args) {

    //+符号表示匹配前面的字符一次或多次
    //^和$是正则表达式中的两个特殊符号,分别表示字符串的开头和结尾。在匹配时,如果不加这两个符号,那么就有可能会出现匹配到部分符合要求的字符串的情况。


    String regex = "^[0-9]+$";
    String input = "123"; // 假设输入为 "123"

    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(input);

    if (matcher.matches()) {
      System.out.printf("%s 匹配结果: %b%n", input, true);
    } else {
      System.out.printf("%s 匹配结果: %b%n", input, false);
    }
  }
}


2> \d{11} 表示单个数字出现 11 次,即 11 位数字
在后面再加上量词,就可以表示单个的数字出现了几次。如果文本中只有姓名和手机号,我们就可以利用这个查找出文本中的手机号了。

张三 11380013800
李四 13500138000
王五 15900138000

2. 空白符
除了特殊单字符外,你在处理文本的时候肯定还会遇到空格、换行等空白符。如换行符 \n,TAB 制表符 \t 等。
不同的系统在每行文本结束位置默认的“换行”会有区别。比如在 Windows 里是 \r\n,在 Linux 和 MacOS 中是 \n。
在正则中,也是类似于 \n 或 \r 等方式来表示空白符号。平时使用正则,
大部分场景使用 \s 就可以满足需求,\s 代表任意单个空白符号。

    \r 回车符
    \n 换行符
    \f 换页符
    \t 制表符
    \v 垂直制表符
    \s 任意空白符

3. 量词
需要匹配单个字符,或者某个部分“重复 N次”“至少出现一次”“最多出现三次”等等这样的字符,
这就需要用到表示量词的元字符了。英文的星号(*)代表出现 0 到多次,加号(+)代表 1 到多次,问号(?)代
表 0 到 1 次,{m,n}代表 m 到 n 次。

比如,在文本中“颜色”这个单词,可能是带有u的colour,也可能是不带u的color,
我们使用 colou?r 就可以表示两种情况了。 todo 如果两个以上的单词呢? --括号括起来

    *:    0到多次
    +:   1到多次
    ?:   0到1次,如colou?r
    {m}:  出现m次,  出现一次可以省略
    {m,}: 出现至少m次
    {m,n}:m到n次


4. 范围
学习了量词,我们就可以用 \d{11} 去匹配所有手机号,但同时也要明白,这个范围比较
大,有一些不是手机号的数字也会被匹配上,比如 11 个 0,那么我们就需要在一个特殊的
范围里找符合要求的数字。
再比如,我们要找出所有元音字母 aeiou 的个数,这又要如何实现呢?在正则表达式中,
表示范围的元字符可以轻松帮我们搞定这样的问题。

  |或,如 ab|bc 代表ab或bc
    [...]多选一,括号中任意单个元素
    [a-z]匹配a到z之同任意单介元素(投ASCII表,包含a,z)
    [^…]取反,不能是括号中的任意单个元素

    eg:  比如某个资源可能以 http:// 开头,或者 https:// 开头,也可能以 ftp:// 开头,那么资源的
    协议部分,我们可以使用 (https?|ftp):\/\/ 来表示。  https://regex101.com/r/PnzZ4k/5

^$是正则表达式中的两个特殊符号,分别表示字符串的开头和结尾。在匹配时,如果不加这两个符号,那么就有可能会出现匹配到部分符合要求的字符串的情况。

eg:   第 1 位固定为数字 1;
        第 2 位可能是 3,4,5,6,7,8,9; 
        第 3 位到第 11 位我们认为可能是 0-9 任意数字。

         ^1[3-9]{1}\d{9}$ = ^1[3-9]\d{9}$   匹配13888888888

     

^

匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。

$

匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。

\b匹配单词的开始或结束
^ 和 $    在单行模式下匹配整段输入,匹配字符串的开始/结束, 同 \A 和 \z,在多行模式下匹配行,可以分辨终止子
\A 和 \Z匹配的是整段输入,结尾终止子可有可无,不管在单行模式还是多行模式下
\A 和 \z匹配的是整段输入,完完整整,不偏不倚,不管在单行模式还是多行模式下
元字符同义表示方法示例
*{0,}

ab*

可以匹配

a或 abbb

+{1,}

正则ab+

可以匹配ab或 abbb

但不能匹配a

?{0,1}

正则 (\+86-)?\d{11}

可以匹配

+86-13800138000

或 13800138000

()包括一个整体, \+ 使用转义字符表示+

贪婪模式/非贪婪模式

在正则中,表示次数的量词默认是贪婪模式,模式会尝试尽可能最大长度去匹配。

非贪婪匹配(Lazy)
在量词后面加上英文的问号 (?),正则就变成了 a*?。找到长度最小且满足条件的

独占模式(Possessive)
不管是贪婪模式,还是非贪婪模式,都需要发生回溯才能完成相应的功能。但是在一些场景
下,我们不需要回溯,匹配不上返回失败就好了,它类似贪婪匹配,但匹配过程不会发生回溯,因此在一些场合下性能会更好。独占模式和贪婪模式很像,独占模式会尽可能多地去匹配,如果匹配失败就结束,不会进行回溯,这样的话就比较节省时间。在量词后面加上加号(+)

独占模式性能比较好,可以节约匹配的时间和 CPU 资源,但有些情况下并不能满足需求,要想使用这个模式还要看具体需求(比如我们接下来要讲的案例),另外还得看你当前使用的语言或库的支持程度。独占模式“不吐出已匹配字符”的特性,会使得一些场景不能使用它。另外,只有少数编程语言支持独占模式。

^代表以这个正则开头,$代表以正则结尾。

括号在正则中的功能就是用于分组。可以用括号括起来表示一个整体, 被括号括起来的部分“子表达式”会被保存成一个子组, 可以在括号里面使用 ?: 不保存子组。不保存子组可以提高正则的性能和子组计数时也更不容易出错。可以使用 “反斜扛 + 编号”,即 \number 的方式来进行引(JavaScript 中是通过$编号)

正则示例
保存正则(正则)

\d{15}(\d{3})?  表示后面三位有或无, 可以匹配15/18位 

不保存正则(?:正则)\d{15}(?:\d{3})?


数左括号(开括号)是第几个,就可以确定是第几个子组。命名分组的格式为(?P<分组名>
正则), 不过命名分组并不是所有语言都支持的

分组引用在查找中使用: 

        前面出现的单词再次出现, 以使用 \w+ 来表示一个单词,

        sublime替换:    (\w+) \1,  输入子组的引用 \1  只能匹配出现两次的, 三次以上的替换不了

           内容:  the little cat cat is in the hat hat,we like it.

里面有一些单词连续出现了多次,我们认为连续出现多次的单词应该是一次,比如:
                the little cat cat is in the hat hat hat, we like it.
其中 cat 和 hat 连接出现多次,要求处理后结果是
                the little cat is in the hat, we like it.        
正则:(\w+)(\s\1)+                (\s\1)+ ------  \s任意空白字符, \1表示组引用   +表示1次或多次  
替换:\1

正则中常见的四种匹配模式分:  不区分大小写、点号通配模式、多行模式和注释模式。

使用说明:  模式修饰符是通过 (? 模式标识)正则

不区分大小写的 cat 就可以写成 (?i)cat == [Cc][Aa][Tt] 

尝试匹配两个连续出现的 cat ,即便是第一个cat 和第二个 cat 大小写不一致  (?i)(cat) \1

第一个和第二个区分大小写, 要用括号把修饰符和正则 cat 部分括起来,加括号相当于作用范围的限定,让不区分大小写只作用于这个括号里的内容。  ((?i)cat) \1

匹配真正的“任意”符号的时候,可以使用 [\s\S] 或 [\d\D] 或 [\w\W] 等。

多行匹配模式。通常情况下,^ 匹配整个字符串的开头,匹配整个字符串的结尾。 的匹配行为。
多行匹配模式改变的就是和的匹配行为。

正则中还有 \A 和 \z(Python 中是 \Z) 这两个元字符容易混淆,\A 仅匹
配整个字符串的开始,\z 仅匹配整个字符串的结束,在多行匹配模式下,它们的匹配行为
不会改变,如果只想匹配整个字符串,而不是匹配每一行,用这个更严谨一些。

正则中注释模式是使用(?#comment) 来表示。      (\w+)(?#word) \1(?#word repeat again)

什么是断言呢?简单来说,断言是指对匹配到的文本位置有要求。比如查找 tom,但其他单词tomorrow 中也包含了 tom。正则中提供了一些结构叫做断言,只用于匹配位置,而不是文本内容本身。常见的断言有三种:单词边界、行的开始或结束以及环视

1> 单词边界(Word Boundary)  \b\w+\b

2>   在默认的单行模式(也称为单行模式)下,^ 匹配字符串的开头,$ 匹配字符串的结尾。在多行模式下,^ 和 $ 符号可以匹配每一行的开头或结尾。大部分实现默认不是多行匹配模式,但也有例外,比如 Ruby 中默认是多行模式。对于校验输入数据来说,一种更严谨的做法是,使用 \A 和 \z (Python 中使用\Z)来匹配整个文本的开头或结尾。(由于这种行为可能会导致预期之外的匹配结果,因此在正则表达式中最好不要使用 ^$,尤其是进行多行匹配时。)

3> 环视就是要求匹配部分的前面或后面要满足(或不满足)某种规则

邮政编码的规则是第一位是 1-9,一共有 6 位数字组成。除了文本本身组成符合这 6 位数的规则外,这 6 位数左边或右边都不能是数字。正确: (?<!\d)[1-9]\d{5}(?!\d)

 

在Python 中,可以在正则前面加上小写字母 r 来表示使用原生字符串

不使用 Unicode 编码时,正则会被编译成其它编码表示形
式。比如,在 macOS 或 Linux 下,一般会编码成 UTF-8,而在 Windows 下一般会编码
成 GBK。

Java 中目前还没有原生字符串,正则需要经过字符串转义和正则转义两个步骤,因此在用到反斜扛的地方,比如表示数字的\d,就得在字符串中表示成\\d,转义会让书写正则变得稍微麻烦一些,在使用的时候需要留意一下。 todo  /A /z的作用

import java.util.regex.Matcher;
import java.util.regex.Pattern;
class Main {
    public static void main(String[] args) {

        //方法1,可以不加 \A 和 \z
        System.out.println(Pattern.matches("\\d{4}-\\d{2}-\\d{2}", "2020-06-01"));
        //方法2,可以不加 \A 和 \z
        System.out.println("2020-06-01".matches("\\d{4}-\\d{2}-\\d{2}")); // true
        //方法3,必须加上 \A 和 \z
        Pattern pattern = Pattern.compile("\\A\\d{4}-\\d{2}-\\d{2}\\z");
        System.out.println(pattern.matcher("2020-06-01").find()); // true
            
        //正则切割, \\W 任意非字母下划线
        Pattern pattern2 = Pattern.compile("\\W+");
        for(String s : pattern2.split("apple, pear! orange; tea")) {
            System.out.println(s);
        }
        
    }
}

                                                                                                                                                                                                                                                                                                                     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值