现在位置: 首页 > Julia 教程 > 正文

Julia 正则表达式

正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。

Julia 使用 PCRE 库,支持与 Perl 兼容的正则表达式(Perl-Compatible Regular Expressions)。

Julia 中正则表达式通过 r"..." 形式的非标准字符串字面量创建,正则表达式字面量内部不做字符串插值和转义(双引号除外):

实例

julia> re = r"^\s*(?:#|$)"
r"^\s*(?:#|$)"

julia> typeof(re)
Regex

正则表达式的常见操作分为三类:

操作函数说明
判断是否匹配occursin(re, str)返回 true 或 false
查找匹配内容match(re, str)、eachmatch(re, str)返回匹配对象或迭代器
替换匹配内容replace(str, re => new)将匹配部分替换为新内容

判断匹配:occursin

若要检查正则表达式是否匹配某字符串,就用 occursin:

实例

julia> occursin(r"^\s*(?:#|$)", "not a comment")
false

julia> occursin(r"^\s*(?:#|$)", "# a comment")
true

查找匹配:match

occursin 只返回正确或错误。如果想知道字符串是如何匹配的,要捕获匹配的信息,可以改用 match 函数:

实例

julia> match(r"^\s*(?:#|$)", "not a comment")

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 对象,它记录了模式匹配的子字符串和捕获的子字符串。下面的例子捕获了注释字符 # 后面的非空文本:

实例

julia> m = match(r"^\s*(?:#\s*(.*?)\s*$|$)", "# a comment ")
RegexMatch("# a comment ", 1="a comment")

当调用 match 时,你可以选择指定开始搜索的索引:

实例

julia> m = match(r"[0-9]", "aaaa1aaaa2aaaa3", 1)
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:

实例

julia> m = match(r"(a|b)(c)?(d)", "acd")
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 对象实现了迭代器方法,可以直接解构到变量:

实例

julia> first, second, third = m; first
"a"

命名捕获组

通过使用捕获组的编号或名称对 RegexMatch 对象进行索引,也可实现对捕获的访问:

实例

julia> m = match(r"(?<hour>\d+):(?<minute>\d+)", "12:45")
RegexMatch("12:45", hour="12", minute="45")

julia> m[:minute]
"45"

julia> m[2]
"45"

查找全部匹配:eachmatch

match 只返回第一个匹配,要找到所有匹配,使用 eachmatch:

实例

julia> for m in eachmatch(r"runoob", "runoob RUNOOB runoob.com")
           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> 引用:

实例

# \g<agroup> 引用命名捕获组,\1 引用第一个捕获组,实现单词对调
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扩展模式,忽略空白和 # 注释,便于编写可读的正则

实例

julia> occursin(r"^runoob$"i, "RUNOOB")     # i 标志:忽略大小写
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)),此时拼接的内容需要注意转义。

更多正则表达式语法可以参考:正则表达式 - 教程