# 正则表达式调出

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

目前只有 RegExMatch 和 RegExReplace 支持正则调出.

## 目录

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

## 语法

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

如果省略 Function, 则必须定义默认的正则调出函数名 pcre_callout. 如果没有定义 pcre_callout, 那么省略 Function 的正则调出将被忽略.

## 正则调出函数

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

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

- Match: 接收 RegExMatchInfo 对象, 其中包含到目前为止的匹配信息.
- CalloutNumber: 接收正则调出的 Number.
- FoundPos: 接收当前潜在匹配的位置.
- Haystack: 接收传递给 RegExMatch 或 RegExReplace 的 Haystack.
- NeedleRegEx: 接收传递给 RegExMatch 或 RegExReplace 的 NeedleRegEx.

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

警告: 不支持在 RegExReplace 或 RegExMatch 在被调用期间更改其输入参数, 这可能会导致不可预测的行为.

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

- 如果函数返回 0 或没有返回数值, 则匹配操作如常进行.
- 如果函数返回 1 或更大的数字, 则在当前位置匹配失败, 但继续进行剩余部分的匹配测试.
- 如果函数返回 -1, 则匹配中止.
- 如果函数返回小于 -1, 的值, 则它被视为 PCRE 错误码且匹配中止. 这将导致 RegExMatch 和 RegExReplace 抛出异常; 异常对象的 Extra 属性包含错误码.

例如:

    Haystack := "The quick brown fox jumps over the lazy dog."
    RegExMatch(Haystack, "i)(The) (\w+)\b(?CCallout)")
    Callout(m,*) {
        MsgBox "m[0]=" m[0] "`nm[1]=" m[1] "`nm[2]=" m[2]
        return 1
    }

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

如果在调出期间修改了 RegEx 函数的任何输入参数, 则该行为是未定义的.

## 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, "Ptr")
    subject           := NumGet(A_EventInfo,  8 + A_PtrSize, "Ptr")
    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, "Ptr")
    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). 例如, 下面的模板可以用来调试正则表达式:

    ; 调用带有自动调出选项 C 的 RegExMatch.
    RegExMatch("xxxabc123xyz", "C)abc.*xyz")

    ; 定义默认的正则调出函数.
    pcre_callout(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 原来的值.

在一些可以确定不会发生匹配的情况下, 则会对 PCRE 进行优化来提早中止. 对于在这些情况中的所有正则调出, 可能需要在模式的开始处指定 (*NO_START_OPT) 来禁用这种优化.

dftg