正则表达式

R语言
python
文本处理
正则表达式介绍
作者

不止BI

发布于

2024年2月3日

正则表达式是一种由字符和操作符组成的模式,用来描述字符串的特征,通过正则表达式可以搜索、替换、提取和匹配特定模式的文本数据。

常用元字符

类型 描述
. 匹配除换行符”\n”以外的任意字符
\\ 转义字符,用于匹配元字符时的转义,匹配元字符时用”\\元字符”
^ 匹配字符串的开始位置
$ 匹配字符串的结束位置
* 匹配前一个元素零次或多次
+ 匹配前一个元素一次或多次
? 匹配前一个元素零次或一次
[] 匹配方括号内的任意一个字符
[^] 否定字符类,匹配不在方括号内的任意字符
() 分组操作符,用于分组匹配模式
| 或操作符,匹配多个模式中的任意一个
\d 匹配一个数字字符,等效于[0-9]
\D 匹配一个非数字字符,等效于[^0-9]
\w 匹配一个字母数字字符,等效于[A-Za-z0-9_]
\W 匹配一个非字母数字字符,等效于[^A-Za-z0-9_]
\s 匹配一个空白字符,包括空格、制表符、换行符等
\S 匹配一个非空白字符
[\u4e00-\u9fa5]+ 匹配Unicode编码中文文本

直接匹配

使用stringr

  • str_extract 只找到第一个匹配的部分

  • str_extract_all 寻找所有的匹配

library(stringr)
text <- c("Hello123!@#(+)10%+", "30%-Hello123!@#中(+)", "好Hello123(-)0+")
str_extract(text, "\\d+%")
[1] "10%" "30%" NA   
str_extract_all(text, "\\d+%")
[[1]]
[1] "10%"

[[2]]
[1] "30%"

[[3]]
character(0)
str_extract(text, "\\d+%?")
[1] "123" "30%" "123"
str_extract_all(text, "\\d+%?")
[[1]]
[1] "123" "10%"

[[2]]
[1] "30%" "123"

[[3]]
[1] "123" "0"  

使用re库匹配数据

  • re.search 只找到第一个匹配的部分

  • re.findall 寻找所有的匹配

import re

text = ["Hello123!@#(+)10%+", "30%-Hello123!@#中(+)", "好Hello123(-)0+"]

[re.search(r"\d+%", t).group() if re.search(r"\d+%", t) else None for t in text]
['10%', '30%', None]
[re.findall(r"\d+%", t) for t in text]
[['10%'], ['30%'], []]

零宽断言

零宽断言是一种特殊的正则表达式语法,用于在匹配文本时添加额外的条件,但不包括在最终的匹配结果中。它可以用来指定匹配位置的前后必须满足的条件。常见的零宽断言包括:

  • 正向顺序零宽断言(正向先行断言):(?=…),表示匹配位置的后面必须满足指定条件。

  • 负向顺序零宽断言(负向先行断言):(?!…),表示匹配位置的后面不能满足指定条件。

  • 正向逆序零宽断言(正向后行断言):(?<=…),表示匹配位置的前面必须满足指定条件。

  • 负向逆序零宽断言(负向后行断言):(?<!…),表示匹配位置的前面不能满足指定条件。

library(stringr)

x <- c(
  "172.10.20.70(上海-上海)", "192.168.20.10(湖北-武汉)",
  "173.20.10.70(河北-石家庄)"
)
# 提取省份
str_extract(x, pattern = "(?<=\\()\\w+(?=-)")
[1] "上海" "湖北" "河北"
# 提取 IP
str_extract(x, pattern = ".*(?=\\()")
[1] "172.10.20.70"  "192.168.20.10" "173.20.10.70" 
import re

text = [
  "172.10.20.70(上海-上海)", "192.168.20.10(湖北-武汉)",
  "173.20.10.70(河北-石家庄)"
]
# 提取省份
[re.search(r"(?<=\()\w+(?=-)",t).group() for t in text]
['上海', '湖北', '河北']
# 提取 IP
[re.search(".*(?=\\()",t).group() for t in text]
['172.10.20.70', '192.168.20.10', '173.20.10.70']

分组捕获

分组捕获是指在正则表达式中使用小括号将一部分模式进行分组,并且可以对分组进行捕获或者不捕获。分组捕获可以帮助我们提取匹配到的部分,或者对匹配到的部分进行后续操作。常见的分组捕获语法包括:

  • 捕获型分组:(pattern),表示对该部分模式进行捕获,匹配到的内容可以在后续操作中使用。

  • 非捕获型分组:(?:pattern),表示对该部分模式进行匹配,但不进行捕获,匹配到的内容不能在后续操作中使用。

# 捕获型分组示例

text <- "apple, banana, cherry"
pattern <- "(\\w+), (\\w+), (\\w+)"
match <- regexec(pattern, text)
if (length(match[[1]]) >= 0) {
  print("捕获型分组示例:")
  matched_text <- regmatches(text, match)[[1]]
  print(str_c("整体匹配结果:", matched_text[1]))
  print(str_c("第一个捕获组:", matched_text[2]))
  print(str_c("第二个捕获组:", matched_text[3]))
  print(str_c("第三个捕获组:", matched_text[4]))
}
[1] "捕获型分组示例:"
[1] "整体匹配结果:apple, banana, cherry"
[1] "第一个捕获组:apple"
[1] "第二个捕获组:banana"
[1] "第三个捕获组:cherry"
# 非捕获型分组示例
text <- "apple, banana, cherry"
pattern <- "(?:\\w+), (\\w+), (\\w+)"
match <- regexec(pattern, text)
if (length(match[[1]]) >= 0) {
  print("\n非捕获型分组示例:")
  matched_text <- regmatches(text, match)[[1]]

  print(str_c("整体匹配结果:", matched_text[1]))
  print(str_c("第一个捕获组:", matched_text[2]))
  print(str_c("第二个捕获组:", matched_text[3]))
}
[1] "\n非捕获型分组示例:"
[1] "整体匹配结果:apple, banana, cherry"
[1] "第一个捕获组:banana"
[1] "第二个捕获组:cherry"
# 捕获后替换
library(stringr)

text <- "apple, banana, cherry"
pattern <- "(\\w+), (\\w+), (\\w+)"
result <- str_replace(text, pattern, "\\1 - \\2 - \\3")
print(result)
[1] "apple - banana - cherry"
import re

# 捕获型分组示例
text = "apple, banana, cherry"
pattern = r"(\w+), (\w+), (\w+)"
match = re.match(pattern, text)
if match:
    print("捕获型分组示例:")
    print("整体匹配结果:", match.group(0))
    print("第一个捕获组:", match.group(1))
    print("第二个捕获组:", match.group(2))
    print("第三个捕获组:", match.group(3))
捕获型分组示例:
整体匹配结果: apple, banana, cherry
第一个捕获组: apple
第二个捕获组: banana
第三个捕获组: cherry
# 非捕获型分组示例
text = "apple, banana, cherry"
pattern = r"(?:\w+), (\w+), (\w+)"
match = re.match(pattern, text)
if match:
    print("\n非捕获型分组示例:")
    print("整体匹配结果:", match.group(0))
    print("第一个捕获组:", match.group(1))
    print("第二个捕获组:", match.group(2))

非捕获型分组示例:
整体匹配结果: apple, banana, cherry
第一个捕获组: banana
第二个捕获组: cherry
# 捕获后替换    
import re

text = "apple, banana, cherry"
pattern = r'(\w+), (\w+), (\w+)'
result = re.sub(pattern, r'\1 - \2 - \3', text)
print(result)
apple - banana - cherry

多行模式

使用多行正则匹配模式可以方便地处理包含换行符的文本,从而实现对多行内容的精确匹配和提取。在多行模式下,会针对每一行进行正则匹配,这在处理日志文件、文本文档等需要跨行匹配的场景中非常有用。

text <- "Line 1\nLine\nLine 3"
pattern <- regex("(Line \\d$)", multiline = TRUE)
matches <- str_extract_all(text, pattern)
print(matches)
[[1]]
[1] "Line 1" "Line 3"
import re

text = "Line 1\nLine\nLine 3"
pattern = r"(Line \d$)"
matches = re.findall(pattern, text, flags=re.MULTILINE)
print(matches)
['Line 1', 'Line 3']

正则推断

inferregex包可以根据字符串自动推断生成正则表达式

# pak::pkg_install("daranzolin/inferregex")
library(inferregex)

s <- "apple, banana, cherry"
infer_regex(s)$regex
[1] "^[a-z]{5},\\s[a-z]{6},\\s[a-z]{6}$"
回到顶部