# 正则表达式调出 [AHK_L 14+]

正则调出提供了在正则表达式模式匹配过程中临时传递控制到脚本的方法. 关于 PCRE 标准调出功能的详细信息, 请参阅 pcre.txt.

正则调出当前只有 RegExMatch() 和 RegExReplace() 支持.

## 目录

- 语法
- 正则调出函数
- EventInfo
- 自动调出
- 备注

## 语法

在 AutoHotkey 中正则调出的语法为 (?CNumber:Function), 其中 Number 和 Function 都是可选的. 只有在指定了 Function 才允许使用冒号, 而如果省略 Number 则冒号也可以省略. 如果指定的 Function 不是自定义的函数名称, 则会出现编译错误且不会开始模式匹配.

如果省略 Function, 则必须在 pcre_callout 变量中指定函数名称. 如果同时存在此名称的全局变量和局部变量, 则局部变量优先. 如果 pcre_callout 没有包含自定义函数的名称, 那么省略了 Function 的正则调出会被忽略.

## 正则调出函数

    Function(Match, CalloutNumber, FoundPos, Haystack, NeedleRegEx)
    {
        ...
    }

正则调出函数最多可以定义 5 个参数:

- Match: 相当于 RegExMatch 中的 OutputVar, 包含需要时数组变量的创建.
- CalloutNumber: 接收正则调出的 Number.
- FoundPos: 接收当前可能匹配的位置.
- Haystack: 接收传递给 RegExMatch() 或 RegExReplace() 的 Haystack.
- NeedleRegEx: 接收传递给 RegExMatch() 或 RegExReplace() 的 NeedleRegEx.

这些名称只是暗示性的. 实际中可以使用其他的名称.

模式匹配是继续进行或失败, 取决于正则调出函数的返回值:

- 如果函数返回 0 或没有返回数值, 则匹配操作如常进行.
- 如果函数返回 1 或更大的数字, 则在当前位置匹配失败, 但继续进行剩余部分的匹配测试.
- 如果函数返回 -1, 则匹配中止.
- 如果函数返回小于 -1 的值, 则它被视为 PCRE 错误码且匹配中止. RegExMatch() 返回空字符串, 而 RegExReplace() 返回原始的 Haystack. 在这两个函数中, ErrorLevel 都包含了错误码.

例如:

    Haystack := "The quick brown fox jumps over the lazy dog."
    RegExMatch(Haystack, "i)(The) (\w+)\b(?CCallout)")
    Callout(m) {
        MsgBox m=%m%`nm1=%m1%`nm2=%m2%
        return 1
    }

在上面的例子中, 对于匹配正则调出之前的那部分模式的每个子字符串都会调用一次 Callout. 使用 \b 排除匹配到不完整的单词, 例如 The quic, The qui, The qu, 等等.

## EventInfo

通过 A_EventInfo 可以使用 pcre_callout_block 结构的附加信息.

    version           := NumGet(A_EventInfo,  0, "Int")
    callout_number    := NumGet(A_EventInfo,  4, "Int")
    offset_vector     := NumGet(A_EventInfo,  8)
    subject           := NumGet(A_EventInfo,  8 + A_PtrSize)
    subject_length    := NumGet(A_EventInfo,  8 + A_PtrSize*2, "Int")
    start_match       := NumGet(A_EventInfo, 12 + A_PtrSize*2, "Int")
    current_position  := NumGet(A_EventInfo, 16 + A_PtrSize*2, "Int")
    capture_top       := NumGet(A_EventInfo, 20 + A_PtrSize*2, "Int")
    capture_last      := NumGet(A_EventInfo, 24 + A_PtrSize*2, "Int")
    pad := A_PtrSize=8 ? 4 : 0  ; 补足 64 位的数据偏移.
    callout_data      := NumGet(A_EventInfo, 28 + pad + A_PtrSize*2)
    pattern_position  := NumGet(A_EventInfo, 28 + pad + A_PtrSize*3, "Int")
    next_item_length  := NumGet(A_EventInfo, 32 + pad + A_PtrSize*3, "Int")
    if (version >= 2)
        mark   := StrGet(NumGet(A_EventInfo, 36 + pad + A_PtrSize*3, "Int"), "UTF-8")

想了解更多信息, 请参阅 pcre.txt, NumGet() 和 A_PtrSize.

## 自动调出

在模式的选项中包含 C 来启用自动调出模式. 在这种情况下的调出相当于在模式里的每项前插入了 (?C255). 例如, 下面的模板可以用来调试正则表达式:

    ; 设置默认的正则调出函数.
    pcre_callout := "DebugRegEx"

    ; 使用自动调出选项 C 来调用 RegExMatch.
    RegExMatch("xxxabc123xyz", "C)abc.*xyz")

    DebugRegEx(Match, CalloutNumber, FoundPos, Haystack, NeedleRegEx)
    {
        ; 请参阅 pcre.txt 了解这些字段的说明.
        start_match       := NumGet(A_EventInfo, 12 + A_PtrSize*2, "Int")
        current_position  := NumGet(A_EventInfo, 16 + A_PtrSize*2, "Int")
        pad := A_PtrSize=8 ? 4 : 0
        pattern_position  := NumGet(A_EventInfo, 28 + pad + A_PtrSize*3, "Int")
        next_item_length  := NumGet(A_EventInfo, 32 + pad + A_PtrSize*3, "Int")

        ; 指出 >>当前匹配<<.
        _HAYSTACK:=SubStr(Haystack, 1, start_match)
            . ">>" SubStr(Haystack, start_match + 1, current_position - start_match)
            . "<<" SubStr(Haystack, current_position + 1)

        ; 指出 >>要计算的下一个项目<<.
        _NEEDLE:=  SubStr(NeedleRegEx, 1, pattern_position)
            . ">>" SubStr(NeedleRegEx, pattern_position + 1, next_item_length)
            . "<<" SubStr(NeedleRegEx, pattern_position + 1 + next_item_length)

        ListVars
        ; 按下 Pause 继续.
        Pause
    }

## 备注

正则调出在当前的半线程中执行, 但在正则调出函数返回后将恢复 A_EventInfo 原来的值. 在 RegExMatch() 或 RegExReplace() 返回时才会设置 ErrorLevel 的值.

在一些可以确定不会发生匹配的情况下, 则会对 PCRE 进行优化来提早中止(即此时会不进行匹配而直接返回 "没有匹配" 的结果). 对于在这些情况中的所有正则调出, 可能需要在模式的开始处指定 (*NO_START_OPT) 来禁用这种优化. 此选项需要 [v1.1.05] 或更高版本.

dftg