正则表达式及其在Java和Python中的应用

学了很多遍正则表达式,总是一学就忘quq,这次,我一定要,记住

正则表达式相关知识

所谓正则表达式,就是用一种格式去表达你想要的字符串的模式,然后用来比较、匹配和查找。

基本语法

原子

原子就是字符串中不可分割的部分,一个字符是一个原子,一个转义字符(例如"")也是一个原子。

递归的定义:()括起来的多个原子(或者说一个正则表达式)构成一个原子。

例如(ab)代表原子ab,在匹配时,原子内部的内容代表一个整体,不可拆分

中括号和花括号

[]括起来的原子代表匹配时匹配上其中的任何一个就行

  • [^]为非原子表,它所能匹配的东西刚好取不加^时的补集
  • 另一种语法是[a-z]代表az之间(在ASCII表中)的所有字符

{}可以放原子或者中括号表达式后面,里面写上数字,表示前面的原子出现的次数

数字的形式可以是 {m} {m,n} {m,}(闭区间)

特殊字符

转义字符

转义字符就是用一个字符代表某一类字符

字符 含义
\d 匹配一个数字
\w 匹配字母数字或下划线
\s 匹配一个空白字符,相当于[\f\n\r\t\v]

这几种字符如果后面字母换成大写就代表取反(\S等价于 [^\s]

元字符

元字符是有特殊含义的字符,如果要在正则表达式中包含元字符本身,必须在其前加上””进行转义

字符 含义
* 前面的原子匹配任何次
+ 前面的原子匹配一次或以上
前面的原子匹配0或1次
^ 匹配字符串首的位置
$ 匹配字符串尾的位置
\b 匹配字符串的边界(\B反)
. 除换行符外的任何字符

可以看到,这些元字符或者对其前面字符的匹配模式有所影响,或者匹配某个位置

模式修正符

还有一个东西叫模式修正符,表示匹配的模式(如忽略大小写这些)。

最常用的就是忽略大小写了,其他的我也不懂,等用到的时候补充。

修饰符 描述
I 忽略大小写
L 做本地化识别匹配
M 多行匹配
S 使.匹配包括换行在内的所有字符
U 根据Unicode字符集解析字符
X 详细模式,正则表达式可以有多行

有的时候你看到的正则表达式是写在两个斜杠之间的,比如/abc/这个斜杠其实没有什么意思,只是表达这是一条正则表达式,在编程的时候不用管这两个斜杠,只用把两个杠之间的东西当做pattern就行。

一般在写正则表达式的时候,会把模式修正符放在右斜杠的右边,比如/abc/i表示匹配这个字符串并且忽略大小写。

捕获组

这里就要介绍()的另一个含义:

被括号括起来的东西叫模式单元,在匹配结束后,系统自动将模式单元中的匹配储存起来,可以进行引用。

当需要指定一个模式单元不被储存的时候,可以用(?: )

命名捕获组(?<name>expression) (在Python中是(?P<name>expression)

捕获组的编号

零号一般代表整个匹配结果,从左到右根据(出现的先后编号

不过要是括号比较多的话,建议使用命名捕获组

非贪婪匹配

如果用表达式(.*)去匹配一个字符串,应该能匹配一整行,因为正则表达式默认贪婪匹配,也就是说,在满足条件的情况下尽可能多的匹配字符。

但是如果我们不想要匹配这么多字符呢?

一种解决方法是用{数字}替代元字符,这样就能指定匹配的次数,但是要是预先不知道次数呢?

可以使用非贪婪匹配

代表匹配次数的元字符后面加上?表示非贪婪匹配,含义是在满足条件的情况下尽可能少的匹配。

例如:"(\d*?)(0*)"匹配字符串后面的0


了解了这些正则表达式中的基本知识,下面来看看这些功能在具体的编程语言中应该怎么实现。

Python的Re库

主要用到的函数

  • re.compile(pattern)

    可输入输入字符,转化为匹配对象

    在需要多次使用一个pattern的时候最好先compile而不是直接拿字符串去匹配,这样能加快速度。

    第二个参数的位置可以输入模式修正符,例如:

    1
    2
    pattern = re.compile(r"abc",re.I)
    #还有一件事:上面的字符串前面有一个r,代表这个字符串不进行转义
  • re.***(pattern,string)

    这里的pattern可以是字符串,也可以是用compile创建的模式对象

    下面这几个函数的调用都是上面的格式:

    • re.findall():搜索所有满足条件的字符串,返回一个列表

    • re.match():从第一个字符开始匹配,返回匹配对象

      可以用group()查看匹配到的结果,用start(),end()获取匹配的开始以及结束位置

    • re.fullmatch(): 用pattern去匹配整个字符串

    • re.search():搜索第一个满足条件的字符串,查找到第一个停止

  • re.sub(pattern,rep,string,mood)

    pattern 为需要寻找的模式

    rep 是要替换为的东西

    string 原字符串

    mood选项(用来控制替换的数量等)

    小例子:

    就是说咱有多懒,写个脚本把字符串中的"{name}"这些东西换成 "%s"(OO作业

    1
    2
    3
    4
    5
    6
    7
     >import  re
    >s = input()
    >while (s != "q"):
    s = input()
    pattern = "{.*?}" # 非贪婪
    ss = re.sub(pattern, "%s", s)
    print( ss + "\\n")

Java与字符串

不用正则表达式的简单处理

  • split()用额定字符串分割字符串, 不会输出空字符串
  • indexOf()搜索字符串出现的位置
  • subString()截取子串
  • trim()删除前后空格以及换行符
  • 判断字符串相等不能用==(而Python中是可以的),用==只是判断地址。应该用s1.equals(s2)判断

正则表达式

  • Pattern类

    Pattern 类对象是由Pattern.compile()函数返回的

  • Matcher类

    由Pattern对象的matcher()方法返回

    方法:

    • group()输入捕获组的编号或者名称,返回对应的字符串

    • start([int group]) 可以输入group的编号,如果不输入参数,默认

方法

  • 匹配方法

    在调用group(),start()等方法获取信息之前,要先进行匹配,否则会报错

    • find() 从上一次匹配到的字符串结束的地方开始匹配

      例如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      String REGEX = "csc";
      String INPUT =
      "cscscore";
      Pattern p = Pattern.compile(REGEX);
      Matcher m = p.matcher(INPUT);
      for (int i=0;i<2;i++) {
      // 第二次匹配是从位置3开始的,所以第二次是匹配不到的
      if(m.find())
      System.out.println(m.start());
      else
      System.out.println("Not found");
      }

      find还可以输入一个int参数,表示匹配开始的位置

    • lookingAt() 从头开始匹配

    • matches()

      相当于Python里面的fullmatch() ,尝试将整个区域与模式匹配

  • 替换方法

    • replaceFirst()

    • replaceAll()

    • appendReplacement() 和appendTail()

      看一个例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public class RegexMatches
      {
      private static String REGEX = "a*b";
      private static String INPUT = "aabfooaabfooabfoobkkk";
      private static String REPLACE = "-";
      public static void main(String[] args) {
      Pattern p = Pattern.compile(REGEX);
      Matcher m = p.matcher(INPUT);
      StringBuffer sb = new StringBuffer();//新建一个空的StringBuffer

      while(m.find()){
      m.appendReplacement(sb,REPLACE);
      }
      m.appendTail(sb);
      System.out.println(sb.toString());
      //输出为-foo-foo-foo-kkk
      }
      }

      appendReplacement就是把从上一次匹配结束(或者0位置)到这一次匹配结束并且替换后的这一段字符串加到StringBuffer结尾

      appendTail就是把上一次匹配结束的位置到结尾的部分都加到StringBuffer结尾