# 正则表达式 `re/*` 是由 PCRE2 支持的正则表达式命名空间。模式是普通字符串,按需编译并缓存;`re/find` 及其同类函数以码点索引报告匹配位置——与 `len` 和 `slice` 使用同一坐标系统——因此结果可以直接反馈到字符串操作中。这些函数是标准库的一部分,无需导入。正则字面量也直接出现在 `match` 分支中;参见模式匹配。 ## 函数 | 函数 | 用途 | |---|---| | `re/match s pattern` | 若模式在 `s` 中任意位置匹配则为 `true`(搜索,非全字符串匹配) | | `re/find s pattern` | 首个匹配,形式为 `%{ :match :start :end :groups }`,否则为 nil | | `re/find-all s pattern` | 不重叠匹配映射的列表 | | `re/replace s pattern replacement` | 替换首个匹配;支持 `$1`、`${name}` 反向引用 | | `re/replace-all s pattern replacement` | 替换每个匹配;支持 `$1`、`${name}` 反向引用 | | `re/split s pattern` | 在每个匹配处分割;不保留捕获组 | | `re/captures s pattern` | 捕获字符串列表(`0` = 完整匹配),否则为 nil | | `re/find-at s pattern offset` | 在码点 `offset` 处或之后的首个匹配 | | `re/escape text` | 转义正则元字符,使 `text` 按字面匹配 | | `re/clear-cache` | 从内部缓存中释放所有已编译模式 | | `re/scan-first s patterns offset` | `patterns`(字符串向量)中恰好在码点 `offset` 处匹配的第一个;返回带 `:index` 的映射,否则为 nil | 说明: - `re/find` 返回 `%{ :match ... :start N :end N :groups ... }`,其中 `:start` / `:end` 是码点切片坐标,因此 `[chars/slice $s $start $end]` 可提取匹配文本。`:groups` 对于命名捕获(`(?<name>...)`)是映射,对于位置组是向量,而当模式没有组时为 `@{}`。 - `re/captures` 先返回索引 0 处的完整匹配,随后是位置捕获组。对于命名捕获,请使用 `re/find`——其 `:groups` 映射以名称为键。 - `re/match` 是普通搜索:`[re/match "abc123" "\d+"]` 为 `true`。当需要全字符串测试时,请用 `\A...\z`(或配合适当标志的 `^...$`)锚定。 - 无效模式不会抛出异常:`re/match` 返回 `false`,而 find / replace / split 系列返回 nil。 - `re/scan-first` 是锚定的:每个模式只在给定偏移处测试(PCRE2 锚定模式),且索引最小的模式胜出。自行推进偏移来对一行分词——它是编辑器语法高亮器背后的原语。 - 已编译的模式存放在固定大小的每线程 LRU 缓存中,因此热点模式只编译一次。`re/clear-cache` 丢弃整个缓存,这对于动态构建模式的长运行进程很重要。 ## 示例 ### 校验与定位
# Search semantics — the pattern may match anywhere in the subject
echo [re/match "user@example.com" "^\\w+@\\w+\\.\\w{2,}$"]   # true

set m [re/find "order 1234 shipped" "\\d+"]
echo $m~match $m~start $m~end                                # 1234 6 10

# :start/:end are codepoint indices — slice the match back out
echo [chars/slice "order 1234 shipped" $m~start $m~end]      # 1234
### 捕获
# Positional captures: index 0 is the full match
echo [re/captures "2024-01-15" "(\\d+)-(\\d+)-(\\d+)"]
# => @{2024-01-15 2024 01 15}

# Named captures arrive as a keyword-keyed map on :groups
set m [re/find "2024-01-15" "(?<y>\\d+)-(?<m>\\d+)-(?<d>\\d+)"]
echo $m~groups~y "-" $m~groups~m "-" $m~groups~d             # 2024-01-15
### 替换与分割
echo [re/replace "a1b22c" "\\d+" "<$1>"]                     # a<1>b22c
echo [re/replace-all "a1b22c" "\\d+" "#"]                    # a#b#c
echo [re/replace-all "john smith" "(?<f>\\w+) (?<l>\\w+)" "${l}, ${f}"]
# => "smith, john"
echo [re/split "a1b22c" "\\d+"]                              # @{a b c}
echo [re/escape "a.b*c"]                                     # a\.b\*c
### 用 scan-first 进行锚定分词
set line "let x = 42"
set patterns @{"\\s+" "let" "\\w+" "=" "[0-9]+"}
set pos 0
while ($pos < $line(len)) {
    set hit [re/scan-first $line $patterns $pos]
    if [nil? $hit] { break }
    echo $hit~index $hit~match
    set pos $hit~end
}
`re/scan-first` 返回恰好在 `pos` 处匹配的第一个模式,让调用者逐个 token 遍历该行。 ## 另见 - 模式匹配 — `match` 中的正则模式 - 字符串 — 码点索引与切片 - 字符 — 单字符分类