小羽

3 minute read

在编程中,我们经常遇到字符串的各种处理问题:如在字符串中找出某个子字符串,亦或者字符串是否匹配某种规则。大量繁琐的规则判断令人焦头烂额,而正则表达式(Regular Expression)正是解决此类问题的一种功能强大的武器。这篇文章介绍了正则表达式的定义、匹配规则以及在java和python中的使用。

I. 定义

正则表达式是好用字符串描述的一种匹配规则,使用正则表达式可以快速判定给定的字符串是否符合匹配规则,除此之外还可以从字符串中搜索、提取、替换等符合规则的子字符串。

举例来说,当我们想要判断一串字符串数字是否符合电话号码的数字规则时(3位区号-8位数字,如027-12345678),可以使用正则表达式:”\d{3}-\d{8}“来进行判断。

II. 匹配规则

正则表达式的匹配规则是从左向右按规则匹配,常用的匹配规则如下:

图片名称

图片名称

III. 在编程语言中的使用

a. Java中的使用

在java标准库java.util.regex内建了正则表达式引擎。下面我来给大家一一讲述正则表达式不同功能在java中的实现。

1. 规则匹配

String.matches(regex)函数可以直接判断当前String是否符合input中正则表达式的规则。

例子:

public class Main {
  public static void main(String[] args){
    String re = "java|python";		//正则表达式

    System.out.println("java".matches(re));		//true
    System.out.println("python".matches(re));	//true
    System.ou..println("C++".matches(re));		//false
  }
}

2. 分组匹配

我们可以用(...)来讲正则表达式划分为不同的子规则,而每个子规则就是一个分组。如果我们想提取一串字符串中符合某个子分组规则的子字符串时,可以使用分组匹配。下面我用一个例子来详细讲解分组匹配的使用方法:

import java.util.regex.*;

public class Main {
  public static void main(String[] args){
    Pattern pattern = Pattern.compile("(\\d{3})\\-(\\d{8})");
    Matcher matcher = pattern.matcher("027-12345678");
    
    if (matcher.matches()) {							//判断是否匹配
      String whole = matcher.group(0);		//提取整个String:"027-12345678"
      String group1 = matcher.group(1);		//提取符合第一组子规则的SubString:"027"
      String group2 = matcher.group(2);		//提取符合第二组子规则的SubString:"12345678"
    } else{
      System.out.println("Matching failed.")
    }
  }
}

Note:在java的字符串中,”\“需要用”\\“来表示。

在这个例子中,我们首先创建了Pattern对象pattern并用Pattern.compile(regex)创建了正则表达式。然后我们创建一个Matcher对象matcher,并调用pattern.matcher(String string)用来输入需要进行判断的字符串。接着我们用Matcher对象的matches( )函数来判断字符串是否匹配规则,如果匹配成功就使用Matcher对象的group(int num)方法来提取子字符串。

3. 非贪婪匹配

当我们使用分组匹配时,会遇到这样的问题:当我们想把数字“987000”分为“987”和“000”两组时,很容易的就想到了正则表达式:”(\d+)(0*)“并进行分组提取。但是这样得到的最终结果是group1 = “987000”, group2 = ““。因为”000”也是数字,而第一组规则匹配时进行了贪婪匹配。当我们想要使用非贪婪匹配的时候需要用到关键字”?“。所以,我们应该使用的正则表达式为:”(\d+?)(0*)“。例子如下:

import java.util.regex.*;

public class Main {
  public static void main(String[] args){
    Pattern pattern = Pattern.compile("(\\d+)(0*)");
    Pattern pattern2 = Pattern.compile("(\\d+?)(0*)");
    Matcher matcher = pattern.matcher("987000");
    Matcher matcher2 = pattern2.matcher("987000");
    
    if (matcher.matches()) {							//判断是否匹配
      String group1 = matcher.group(1);		//提取符合第一组子规则的SubString:"987000"
      String group2 = matcher.group(2);		//提取符合第二组子规则的SubString:""
    } 
    
    if (matcher2.matches()) {							//判断是否匹配
      String group1 = matcher2.group(1);		//提取符合第一组子规则的SubString:"987"
      String group2 = matcher2.group(2);		//提取符合第二组子规则的SubString:"000"
    } 
    
  }
}

Note:当出现??(如d??)时,前后两个?意义不同。前一个?代表字符可以出现1次或0次,后一个字符代表非贪婪匹配。

4.分割字符串

String.split(regex)方法可以用输入的正则表达式分割字符串。例子如下:

"a b c".split("\\s"); // { "a", "b", "c" }
"a b  c".split("\\s"); // { "a", "b", "", "c" }
"a, b ;; c".split("[\\,\\;\\s]+"); // { "a", "b", "c" }

5. 搜索字符串

Matcher对象的find( )方法可以搜索字符串,并用此对象的start( )end( )方法提取它。例子如下:

public class Main {
    public static void main(String[] args) {
        String s = "the quick brown fox jumps over the lazy dog.";
        Pattern p = Pattern.compile("\\wo\\w");						//两个字母中有o的情况
        Matcher m = p.matcher(s);
        while (m.find()) {
            String sub = s.substring(m.start(), m.end());
            System.out.println(sub);		//row, fox, dog
        }
    }
}

6. 替换字符串

String.replaceAll(regex, string)方法可以用正则表达式找到匹配规则的子字符串并用新的string来替换。例子如下:

public class Main {
    public static void main(String[] args) {
        String s = "The     quick\t\t brown   fox  jumps   over the  lazy dog.";
        String r = s.replaceAll("\\s+", " ");
        System.out.println(r); // "The quick brown fox jumps over the lazy dog."
    }
}

6.1 反向引用

可以使用$1, $2(数字代表是第几组)在替换字符串时进行反向引用。例如,当我们想在找到的字符串前后加上<b>xxxx<\b>可以使用如下代码:

public class Main {
    public static void main(String[] args) {
        String s = "the quick brown fox jumps over the lazy dog.";
        String r = s.replaceAll("\\s([a-z]{4})\\s", " <b>$1</b> ");
        System.out.println(r);	//the <b>k</b> brown fox <b>s</b> over the lazy dogs.
    }
}

b. Python中的使用

Python提供了re模块,其中包含了所有正则表达式的功能。由于python字符串本身也用”\“转义,所以在使用正则表达式时最好使用python的r前缀,这样就不用考虑转义的问题了。

1. 规则匹配

match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None。常见的判断方法就是:

import re

test = '用户输入的字符串'
if re.match(r'正则表达式', test):
    print('ok')
else:
    print('failed')

2. 分组匹配

python和java一样,在正则表达式中使用(...)来进行分组,在分组中依旧是使用group()方法。例子如下:

m = re.match(r'^(\d{3})-(\d{8})$', '027-12345678')

m.group(0)		#提取整个字符串:'027-12345678'
m.group(1)		#提取第一组子字符串:'027'
m.group(2)		#提取第二组子字符串:'12345678'

3. 非贪婪匹配

python和java一样,默认是贪婪匹配。需要非贪婪匹配时依旧是使用关键字,例子如下:

re.match(r'^(\d+)(0*)$', '987000').groups()	#('987000', '')
re.match(r'^(\d+?)(0*)$', '987000').groups()	#('987', '000')

4. 分割字符串

re.spit(regex, string)函数用来分割字符串。例子如下:

re.split(r'\s+', 'a b     c')		#['a', 'b', 'c']
re.split(r'[\s\,]+', 'a,b, c   d')	#['a', 'b', 'c', 'd']

5. 搜索字符串

re.search(regex, string)函数用来查找第一个符合正则表达式规则的String。例子如下:

import re
m = re.search(r'def', 'abcdef')
m.group(0)	#def
m.span()	#(3,5)

6. 替换字符串

re.sub(regex, string)用来替换字符串,并返回新的字符串。如果没找到则返回原字符串。例子如下:

import re
text = 'how are    you'
print re.sub('\s+', '-', text)		#how-are-you

IV. 总结

正则表达式是一种功能强大的字符串匹配规则。我们可以使用它对字符串进行匹配、分组匹配与提取、查找、分割、替换等操作。

V. 参考

https://www.liaoxuefeng.com/wiki/1252599548343744/1255945288020320