Julia 正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
Julia 使用 PCRE 库,支持与 Perl 兼容的正则表达式(Perl-Compatible Regular Expressions)。
Julia 中正则表达式通过 r"..." 形式的非标准字符串字面量创建,正则表达式字面量内部不做字符串插值和转义(双引号除外):
实例
r"^\s*(?:#|$)"
julia> typeof(re)
Regex
正则表达式的常见操作分为三类:
| 操作 | 函数 | 说明 |
|---|---|---|
| 判断是否匹配 | occursin(re, str) | 返回 true 或 false |
| 查找匹配内容 | match(re, str)、eachmatch(re, str) | 返回匹配对象或迭代器 |
| 替换匹配内容 | replace(str, re => new) | 将匹配部分替换为新内容 |
判断匹配:occursin
若要检查正则表达式是否匹配某字符串,就用 occursin:
实例
false
julia> occursin(r"^\s*(?:#|$)", "# a comment")
true
查找匹配:match
occursin 只返回正确或错误。如果想知道字符串是如何匹配的,要捕获匹配的信息,可以改用 match 函数:
实例
julia> match(r"^\s*(?:#|$)", "# a comment")
RegexMatch("#")
若正则表达式与给定字符串不匹配,match 返回 nothing——在交互式提示框中不打印任何东西的特殊值。除了不打印,它是一个完全正常的值,可以用程序来测试:
实例
m = match(r"^\s*(?:#|$)", line)
if m === nothing
println("not a comment")
else
println("blank or comment")
end
如果正则表达式匹配,match 的返回值是一个 RegexMatch 对象,它记录了模式匹配的子字符串和捕获的子字符串。下面的例子捕获了注释字符 # 后面的非空文本:
实例
RegexMatch("# a comment ", 1="a comment")
当调用 match 时,你可以选择指定开始搜索的索引:
实例
RegexMatch("1")
julia> m = match(r"[0-9]", "aaaa1aaaa2aaaa3", 6)
RegexMatch("2")
julia> m = match(r"[0-9]", "aaaa1aaaa2aaaa3", 11)
RegexMatch("3")
提取匹配信息
你可以从 RegexMatch 对象中提取如下信息:
| 字段 | 描述 |
|---|---|
m.match | 匹配的整个子字符串 |
m.captures | 捕获的子字符串数组 |
m.offset | 整个匹配开始处的偏移 |
m.offsets | 捕获子字符串的偏移向量 |
当捕获不匹配时,m.captures 在该处包含 nothing,m.offsets 的偏移量为 0:
实例
RegexMatch("acd", 1="a", 2="c", 3="d")
julia> m.match
"acd"
julia> m.captures
3-element Vector{Union{Nothing, SubString{String}}}:
"a"
"c"
"d"
julia> m.offset
1
julia> m.offsets
3-element Vector{Int64}:
1
2
3
# 第二个捕获组 (c)? 没有匹配到内容
julia> m = match(r"(a|b)(c)?(d)", "ad")
RegexMatch("ad", 1="a", 2=nothing, 3="d")
julia> m.captures
3-element Vector{Union{Nothing, SubString{String}}}:
"a"
nothing
"d"
RegexMatch 对象实现了迭代器方法,可以直接解构到变量:
实例
"a"
命名捕获组
通过使用捕获组的编号或名称对 RegexMatch 对象进行索引,也可实现对捕获的访问:
实例
RegexMatch("12:45", hour="12", minute="45")
julia> m[:minute]
"45"
julia> m[2]
"45"
查找全部匹配:eachmatch
match 只返回第一个匹配,要找到所有匹配,使用 eachmatch:
实例
println("Match: $(m.match) at offset $(m.offset)")
end
Match: runoob at offset 1
Match: runoob at offset 14
julia> collect(m.match for m in eachmatch(r"[0-9]+", "1 tomato 2 oranges"))
2-element Vector{SubString{String}}:
"1"
"2"
替换:replace
使用 replace 函数可以把正则匹配到的内容替换为新字符串:
实例
julia> replace("a1b2c3", r"[0-9]" => "#")
"a#b#c#"
# 只替换第一个匹配
julia> replace("a1b2c3", r"[0-9]" => "#" , count=1)
"a#b2c3"
替换字符串中可以利用 \n 引用第 n 个捕获组,此时替换字符串需要加 s 前缀。捕获组 0 指的是整个匹配对象,命名捕获组用 \g<groupname> 引用:
实例
julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"
julia> replace("a", r"." => s"\g<0>1")
"a1"
# 日期格式转换
julia> replace("2024-05-01", r"(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})" => s"\g<d>/\g<m>/\g<y>")
"01/05/2024"
正则表达式标志
你可以在结束双引号的后面的加上 i、m、s 和 x 标志对正则表达式进行修改:
| 标志 | 含义 |
|---|---|
i | 不区分大小写匹配 |
m | 多行模式,^ 和 $ 匹配每一行的开头和结尾 |
s | 单行模式,让 . 也能匹配换行符 |
x | 扩展模式,忽略空白和 # 注释,便于编写可读的正则 |
实例
true
# ism 三个标志一起使用
julia> match(r"a+.*b+.*d$"ism, "Goodbye,\nOh, angry,\nBad world\n")
RegexMatch("angry,\nBad world")
# x 标志:可以分行书写、加注释
julia> match(r"""
(\d{4}) - # 年份
(\d{2}) - # 月份
(\d{2}) # 日
"""x, "2024-05-01")
RegexMatch("2024-05-01", 1="2024", 2="05", 3="01")
提示:如果需要根据变量动态构造正则表达式,可以使用 Regex 构造函数:Regex("Day " * string(day)),此时拼接的内容需要注意转义。
更多正则表达式语法可以参考:正则表达式 - 教程
