Elixir正则表达式:Regex模块的模式匹配
还在为字符串匹配和文本处理而烦恼?Elixir的Regex模块提供了强大而灵活的正则表达式功能,让你的文本处理变得简单高效!本文将深入解析Elixir正则表达式的核心功能和使用技巧,帮助你掌握这一强大的工具。
正则表达式基础
Elixir的正则表达式基于PCRE(Perl Compatible Regular Expressions)标准,构建在Erlang的:re模块之上。在Elixir中,你可以使用~r sigil来创建正则表达式:
# 创建简单的正则表达式
regex = ~r/foo/
"foo" =~ regex # true
"bar" =~ regex # false
# 使用不同的分隔符
~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>
核心功能详解
1. 正则表达式编译
Elixir提供了两种编译正则表达式的方式:
# 安全编译,返回 {:ok, regex} 或 {:error, reason}
{:ok, regex} = Regex.compile("foo")
{:ok, case_insensitive_regex} = Regex.compile("foo", "i")
# 强制编译,失败时抛出异常
regex = Regex.compile!("foo")
2. 模式匹配检测
Regex.match?/2函数用于快速检测字符串是否匹配模式:
# 基本匹配
Regex.match?(~r/foo/, "foo") # true
Regex.match?(~r/foo/, "FOO") # false
Regex.match?(~r/foo/i, "FOO") # true (忽略大小写)
# 边界匹配
Regex.match?(~r/^foo/, "foobar") # true
Regex.match?(~r/foo$/, "barfoo") # true
Regex.match?(~r/^foo$/, "foobar") # false
3. 捕获匹配结果
Regex.run/3函数返回匹配的捕获组:
# 基本捕获
Regex.run(~r/c(d)/, "abcd") # ["cd", "d"]
Regex.run(~r/e/, "abcd") # nil
# 返回索引位置
Regex.run(~r/c(d)/, "abcd", return: :index) # [{2, 2}, {3, 1}]
# 指定捕获选项
Regex.run(~r/c(?<foo>d)/, "abcd", capture: :all_names) # ["d"]
4. 命名捕获
Regex.named_captures/3返回命名捕获组的映射:
# 命名捕获示例
regex = ~r/(?<first_name>\w+) (?<last_name>\w+)/
Regex.named_captures(regex, "John Doe")
# %{"first_name" => "John", "last_name" => "Doe"}
# 返回索引位置
Regex.named_captures(regex, "John Doe", return: :index)
# %{"first_name" => {0, 4}, "last_name" => {5, 3}}
5. 全局扫描匹配
Regex.scan/3查找所有非重叠匹配:
# 查找所有匹配
Regex.scan(~r/c(d|e)/, "abcd abce")
# [["cd", "d"], ["ce", "e"]]
# 使用非捕获组
Regex.scan(~r/c(?:d|e)/, "abcd abce")
# [["cd"], ["ce"]]
# 返回索引
Regex.scan(~r/\w+/, "hello world", return: :index)
# [[{0, 5}], [{6, 5}]]
6. 字符串分割
Regex.split/3使用正则表达式分割字符串:
# 基本分割
Regex.split(~r/,/, "a,b,c") # ["a", "b", "c"]
# 限制分割次数
Regex.split(~r/,/, "a,b,c", parts: 2) # ["a", "b,c"]
# 去除空字符串
Regex.split(~r/,/, ",a,,b,", trim: true) # ["a", "b"]
# 包含捕获组
Regex.split(~r/([,-])/, "a-b,c", include_captures: true)
# ["a", "-", "b", ",", "c"]
7. 字符串替换
Regex.replace/4使用正则表达式进行字符串替换:
# 基本替换
Regex.replace(~r/b/, "abc", "d") # "adc"
# 使用反向引用
Regex.replace(~r/a(b)c/, "abc", "x\\1y") # "xby"
# 使用命名引用
Regex.replace(~r/a(?<middle>b)c/, "abc", "x\\g{middle}y") # "xby"
# 使用函数替换
Regex.replace(~r/\w+/, "hello world", &String.upcase/1)
# "HELLO WORLD"
# 只替换第一个匹配
Regex.replace(~r/b/, "abcbe", "d", global: false) # "adcbe"
高级特性
1. 修饰符选项
Elixir支持多种正则表达式修饰符:
| 修饰符 | 选项 | 描述 |
|---|---|---|
i | :caseless | 忽略大小写 |
m | :multiline | 多行模式 |
s | :dotall | 点号匹配换行符 |
x | :extended | 忽略空白和注释 |
u | :unicode | Unicode模式 |
U | :ungreedy | 非贪婪模式 |
# 多行模式示例
text = "Line 1\nLine 2\nLine 3"
Regex.run(~r/^Line/, text) # nil
Regex.run(~r/^Line/m, text) # ["Line"]
# Unicode模式
Regex.run(~r/\p{L}+/u, "café") # ["café"]
2. 字符类
Elixir支持POSIX字符类:
# 字母数字字符
Regex.match?(~r/^[[:alnum:]]+$/, "abc123") # true
# 空白字符
Regex.match?(~r/^[[:space:]]+$/, " \t\n") # true
# 组合字符类
Regex.match?(~r/^[[:alnum:][:punct:]]+$/, "abc!123") # true
3. 转义特殊字符
Regex.escape/1函数用于转义正则表达式中的特殊字符:
Regex.escape(".*+?^$()[]{}|\\")
# "\\.\\*\\+\\?\\^\\$\\(\\)\\[\\]\\{\\}\\|\\\\"
# 实际应用
search_term = "file.txt"
regex = Regex.compile!("^" <> Regex.escape(search_term) <> "$")
Regex.match?(regex, "file.txt") # true
实战示例
1. 邮箱验证
defmodule EmailValidator do
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
def valid?(email) do
Regex.match?(@email_regex, email)
end
end
EmailValidator.valid?("user@example.com") # true
EmailValidator.valid?("invalid.email") # false
2. URL解析
defmodule URLParser do
@url_regex ~r/^(https?):\/\/([^\/:]+)(?::(\d+))?(\/.*)?$/
def parse(url) do
case Regex.run(@url_regex, url) do
[_, protocol, host, port, path] ->
%{
protocol: protocol,
host: host,
port: port || default_port(protocol),
path: path || "/"
}
nil -> nil
end
end
defp default_port("http"), do: "80"
defp default_port("https"), do: "443"
end
URLParser.parse("https://example.com:8080/path")
# %{protocol: "https", host: "example.com", port: "8080", path: "/path"}
3. 日志分析
defmodule LogParser do
@log_regex ~r/^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>\w+)\] (?<message>.+)$/
def parse_line(line) do
case Regex.named_captures(@log_regex, line) do
%{"timestamp" => ts, "level" => level, "message" => msg} ->
{:ok, %{timestamp: ts, level: String.to_atom(level), message: msg}}
nil -> :error
end
end
def filter_by_level(logs, level) when is_binary(level) do
level = String.to_atom(level)
Enum.filter(logs, &(&1.level == level))
end
end
性能优化技巧
1. 预编译正则表达式
# 在模块属性中预编译
defmodule MyApp.Utils do
@email_regex Regex.compile!(~r/.../)
@phone_regex Regex.compile!(~r/.../)
def validate_email(email), do: Regex.match?(@email_regex, email)
def validate_phone(phone), do: Regex.match?(@phone_regex, phone)
end
2. 使用非捕获组
# 使用捕获组(较慢)
Regex.run(~r/(abc)|(def)/, "abc") # ["abc", "abc", nil]
# 使用非捕获组(较快)
Regex.run(~r/(?:abc)|(?:def)/, "abc") # ["abc"]
3. 避免过度使用回溯
# 避免灾难性回溯
# 不好的写法:~r/(a+)+b/
# 好的写法:~r/a+b/
常见问题与解决方案
1. 处理Unicode字符
# 错误:不处理Unicode
Regex.run(~r/./, "café") # ["c"] (只匹配第一个字节)
# 正确:使用Unicode模式
Regex.run(~r/./u, "café") # ["c"] (匹配第一个字素)
Regex.run(~r/\X/u, "café") # ["café"] (匹配整个字素)
2. 多行文本处理
text = """
Line 1
Line 2
Line 3
"""
# 匹配每行的开头
Regex.scan(~r/^Line/m, text) # [["Line"], ["Line"], ["Line"]]
# 匹配整个文本的开头
Regex.scan(~r/^\ALine/m, text) # [["Line"]]
3. 性能监控
defmodule RegexBenchmark do
def measure_performance(regex, text, iterations \\ 1000) do
{time, _} = :timer.tc(fn ->
for _ <- 1..iterations, do: Regex.run(regex, text)
end)
time / iterations
end
end
总结
Elixir的Regex模块提供了强大而灵活的正则表达式处理能力,涵盖了从简单的模式匹配到复杂的文本处理的各种场景。通过合理使用预编译、非捕获组和适当的修饰符,你可以构建高效且可维护的文本处理逻辑。
记住这些最佳实践:
- 预编译常用的正则表达式
- 使用命名捕获提高代码可读性
- 合理选择修饰符优化匹配行为
- 处理Unicode文本时使用
u修饰符 - 监控性能并及时优化复杂模式
掌握了Elixir正则表达式的这些技巧,你将能够轻松应对各种文本处理挑战,构建更加健壮和高效的应用程序。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



