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

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

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

目录

语法

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

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

正则调出函数

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

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

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

警告: 在调用期间更改 RegExReplaceRegExMatch 的输入参数是不受支持的, 可能会导致不可预测的行为.

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

例如:

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] 或更高版本.