函数
目录
- 入门和简单示例
- 参数
- 可选参数
- 返回值给调用者
- 可变参数函数
- 局部变量
- 动态调用函数
- 优化布尔求值
- 在函数中使用子程序
- Return, Exit 及一般说明
- 使用 #Include 在多个脚本间共享函数
- 函数库: 标准库和用户库
- 内置函数
入门和简单示例
函数类似于子程序 (Gosub), 不过它可以从调用者那里接受参数 (输入). 同时, 函数还可以返回值给其调用者. 思考后面这个接受两个数字并返回它们的和的简单函数:
Add(x, y) { return x + y ; "Return" 期望 表达式. }
上面的被称为函数 定义, 因为它创建了一个名称为 "Add" (不区分大小写) 的函数并且确立了调用它时必须准确的提供两个参数 (x 和 y). 要调用此函数, 请把它的结果通过 := 运算符 赋值给变量. 例如:
Var := Add(2, 3) ; 数字 5 将被保存到 Var.
并且, 调用函数时可以不保存其返回值:
Add(2, 3)
但在这种情况下, 函数返回的任何值会被丢弃; 所以除非函数产生了返回值外的其他效果, 否则这个调用没有意义.
由于函数调用是 表达式, 在其参数列表中的任何变量名称都不应该括在百分号中. 与之相比, 原义的字符串应该括在双引号中. 例如:
if InStr(MyVar, "fox") MsgBox The variable MyVar contains the word fox.
最后, 可以在任何命令的参数中调用函数 (除了像 StringLen 的中的那些 OutputVar 和 InputVar 参数). 然而, 不支持 表达式 的参数必须加上 "% " 前缀, 例如:
MsgBox % "The answer is: " . Add(3, 2)
在原生支持表达式的参数中也允许加上 "% " 前缀, 不过它会被简单的忽略了.
参数
定义函数时, 其参数都在其名称后面的括号中列出 (在其名称和开括号之间不能含有空格). 如果函数不接受任何参数,请把括号留空,例如:GetCurrentTimestamp()
。
ByRef 参数: 从函数的角度看, 参数本质上是 局部变量, 除非它们被定义为 ByRef, 例如:
Swap(ByRef Left, ByRef Right) { temp := Left Left := Right Right := temp }
在上述例子中, ByRef 的使用让每个参数变成从调用者传递进来的变量的一个别名. 换句话说, 参数和调用者的变量都引用内存中相同的内容. 这样使得 Swap 函数可以通过移动 Left 的内容到 Right 中来修改调用者的变量, 反之亦然.
与之相比, 在上述例子中如果没有使用 ByRef, Left 和 Right 将是调用者变量的副本, 因此 Swap 函数不会对外部产生影响.
由于 return 只能送回一个值给函数的调用者, 所以可以使用 ByRef 送回更多的结果. 这是由函数向调用者传递进来的变量 (通常为空) 储存一个值来实现的.
传递大字符串给函数时, 使用 ByRef 提高了性能并且通过避免生成字符串的副本节约了内存. 同样地, 使用 ByRef 送回长字符串给调用者通常比类似 Return HugeString
的方式执行的更好.
[AHK_L 60+]: 如果传递给 ByRef 参数的不是可修改的变量, 那么函数会表现的就像关键字 "ByRef" 没有那样. 例如, Swap(A_Index, i)
保存 A_Index 的值到 i 中, 但是当 Swap 函数返回时赋给 Left 的值会被丢弃.
[v1.1.01+]:IsByRef() 函数可以用来判断调用者是否为指定的 ByRef 参数提供了变量。
已知限制:
- 对象字段在 ByRef 的目的中不会被视为变量. 例如, 如果
foo.bar
传递给 ByRef 参数, 那么它将表现的就像 ByRef 没有那样. - 不能传递 Clipboard, 内置变量 或 环境变量 给函数的 ByRef 参数, 即使脚本中没有 #NoEnv 时.
- 尽管函数可以递归调用它自己,但是如果它传递它自己的一个局部变量或非 ByRef 参数给自己的 ByRef,那么新一层的 ByRef 参数将引用它自己那个名称的局部变量而不是之前层的。然而, 当函数传递给它自己 全局变量, 静态变量 或 ByRef 参数时不会产生这样的问题.
- 如果一个参数在函数调用中被解析为一个变量 (例如
Var
或++Var
或Var*=2
), 它左边或右边的其他参数可能在它被传递给函数前修改这个变量. 例如, 当 Var 初始为 0 时func(Var, Var++)
会意外地传递 1 和 0, 即使函数的首个参数不是 ByRef 类型时. 因为这种行为是违反常规的, 所以可能在将来的版本中改变.
可选参数
定义函数时, 可以把它的一个或多个参数标记为可选的. 这可以通过在参数后添加一个等号(在 v1.1.09+ 中还可以为 :=
)跟着默认值完成. 后面的函数中其 Z 参数被标记为可选的:
Add(X, Y, Z:=0) { return X + Y + Z }
从 v1.1.09 起,=
和 :=
都支持。为了与表达式赋值保持一致且与将来的 AutoHotkey 版本兼容,建议使用后者。
当调用者传递 三个 参数给上面的函数时, Z 的默认值被忽略. 但当调用者仅传递 两个 参数时, Z 自动接受默认值 0.
可选参数不能孤立地放在参数列表的中间. 换句话说, 在首个可选参数右边的所有参数都必须标记为可选的. [AHK_L 31+]:调用函数时可以省略参数列表中间的可选参数,如下所示:对于动态的函数调用和方法调用,版本为 v1.1.12+ 才支持这种特性。
Func(1,, 3) Func(X, Y:=2, Z:=0) { ; 请注意此时 Z 必须是可选的。 MsgBox %X%, %Y%, %Z% }
在 v1.0.46.13+, ByRef 参数 也支持默认值; 例如: Func(ByRef p1 = "")
. 每当调用者省略这样的参数时, 函数会创建一个包含默认值的局部变量; 换句话说, 函数表现的就像关键字 "ByRef" 没有那样.
参数的默认值必须是下列形式的其中一种:true
、false
、原义的整数、原义的浮点数或引号包围的/原义的字符串例如 "fox" 或 ""(但在 1.0.46.13+ 之前的版本中字符串只支持 "")。
返回值给调用者
Test := returnTest() MsgBox % Test returnTest() { return 123 }
如果要从函数中返回额外的结果,可以使用 ByRef:
returnByRef(A,B,C) MsgBox % A "," B "," C returnByRef(ByRef val1, ByRef val2, ByRef val3) { val1 := "A" val2 := 100 val3 := 1.1 return }
[v1.0.97+]: 可以使用对象和数组返回多值甚至是命名值:
Test1 := returnArray1() MsgBox % Test1[1] "," Test1[2] Test2 := returnArray2() MsgBox % Test2[1] "," Test2[2] Test3 := returnObject() MsgBox % Test3.id "," Test3.val returnArray1() { Test := [123,"ABC"] return Test } returnArray2() { x := 456 y := "EFG" return [x, y] } returnObject() { Test := {id: 789, val: "HIJ"} return Test }
可变参数函数 [AHK_L 60+]
定义函数时, 在最后一个参数后面写一个星号来标记此函数为可变参数的, 这样让它可以接收可变数目的参数:
Join(sep, params*) { for index,param in params str .= param . sep return SubStr(str, 1, -StrLen(sep)) } MsgBox % Join("`n", "one", "two", "three")
调用可变参数函数时, 通过保存在函数的最后参数中的对象可以访问剩余的参数. 函数的首个剩余参数在 params[1]
, 第二个在 params[2]
等等. 和所有的标准对象一样, 使用 params.MaxIndex()
可以确定最大的索引值 (这里为参数的数目). 但是如果没有参数, MaxIndex 会返回空字符串.
注意:
可变参数函数调用
虽然可变参数函数可以接受可变数目的参数,不过在函数调用中使用相同的语法可以把数组作为参数传递给任何函数:
substrings := ["one", "two", "three"] MsgBox % Join("`n", substrings*)
注意:
- 在源数组中参数的编号从 1 开始.
- 数组中的可选参数可以完全省略.
- 调用自定义函数时数组参数可以包含命名项,在其他情况下则不支持命名项。
- 目标函数也可以是可变的, 此时命名项被复制, 即使它们没有一致的形式参数.
- 这样的语法还可以用于调用对象的方法或获取对象的属性; 例如,
Object.Property[Params*]
. 在 v1.1.12+,它还可以用于设置属性。
已知限制:
- 只有最右边的那个参数才可以这样展开。例如, 支持
Func(x, y*)
但不支持Func(x*, y)
. - 在星号 (
*
) 和参数列表中最后的形式参数间不能存在任何的非空白字符.
局部变量和全局变量
局部变量
默认情况下, 在函数中创建或访问的所有变量都是 局部的 (除了 超级全局 变量和内置变量, 例如 Clipboard, ErrorLevel 和 A_TimeIdle). 每个局部变量的内容只在它所在的函数中可见. 因此, 一个局部变量可以和一个全局变量有相同的名称却有着不同的内容. 最后, 所有的局部变量在每次调用函数时都以空值开始.
全局变量
要在函数中引用现有的全局变量 (或创建新的), 需要在使用前声明此变量为全局的. 例如:
LogToFile(TextToLog) { global LogFileName ; 此全局变量之前已经在函数外的某个地方赋值了. FileAppend, %TextToLog%`n, %LogFileName% }
假设全局模式: 如果函数需要访问或创建大量的全局变量, 通过在函数的首行使用单词 "global" 或声明局部变量可以把函数定义为假设其所有的变量都是全局的 (除了它的参数). 例如:
SetDefaults() { global ; 如果此函数的首行是类似于 "local MyVar" 这样的, 那么这个单词可以省略. MyGlobal := 33 ; 把 33 赋值给全局变量, 必要时首先创建这个变量. local x, y:=0, z ; 在这种模式中局部变量必须进行声明, 否则会假设它们为全局的. }
函数还可以使用这种假设全局模式来创建全局 数组, 例如赋值给 Array%A_Index%
的循环.
超级全局变量 [v1.1.05+]: 如果全局声明出现在任何函数的外面, 默认情况下它可以对所有函数有效. 这样可以避免在每个函数中重复声明变量的需要. 不过, 如果声明了含有相同名称的函数参数或局部变量, 那么它会优先于全局变量. 由 class 关键字创建的变量也是超级全局的.
静态变量
静态变量总是隐式的局部变量, 但和局部变量的区别是它们的值在多次调用期间是记住的. 例如:
LogToFile(TextToLog) { static LoggedLines = 0 LoggedLines += 1 ; 保持局部的计数 (它的值在多次调用期间是记住的). global LogFileName FileAppend, %LoggedLines%: %TextToLog%`n, %LogFileName% }
静态初始化: 在 1.0.46 以前的版本中, 所有的静态变量都是以空值开始; 所以要检查静态变量首次被使用的唯一方法是检查它是否为空值. 在 v1.0.46+,静态变量可以初始化为 ""
外的其他值,通过在后面跟着 :=
或 =
及后面这些形式的其中一种:true
、false
、原义的整数、原义的浮点数或引号包围的/原义的字符串,如 "fox"
。例如:static X:=0, Y:="fox"
。每个静态变量只初始化一次 (在脚本开始执行前).
[AHK_L 58+]: 支持 Static var := expression
. 根据这些表达式在脚本中出现的顺序对它们进行计算,紧接着才进入脚本的自动执行段。
假设静态模式 [v1.0.48+]: 通过在函数的首行使用单词 "static" 可以把函数定义为假设其所有的变量都是静态的 (除了它的参数). 例如:
GetFromStaticArray(WhichItemNumber) { static static FirstCallToUs := true ; 静态声明初始化仍然只运行一次 (在脚本执行前). if FirstCallToUs ; 在首次调用而不在后续的调用时创建静态数组. { FirstCallToUs := false Loop 10 StaticArray%A_Index% := "Value #" . A_Index } return StaticArray%WhichItemNumber% }
在假设静态模式中, 任何非静态变量都必须声明为局部变量或全局变量.
关于局部和全局的更多信息
通过逗号分隔多个变量这样可以在同一行声明它们, 例如:
global LogFileName, MaxRetries := 5 static TotalAttempts = 0, PrevResult
在 v1.0.46+, 通过后面跟着 :=
or =
及任意 表达式 局部或全局变量可以在同一行初始化, 如同声明那样 (在声明时 =
运算符和 :=
作用相同). 与 静态初始化 不同, 在每次调用函数时都会对局部变量和全局变量进行初始化, 但只在控制流实际达到它们所在的语句时. 换句话说, 像 local x = 0
这样的一行和写成单独的两行有同样的效果: local x
后面跟着 x = 0
.
因为单词 local, global 和 static 都是在脚本运行时立即处理的, 所以不能使用 IF 语句 有条件的声明变量. 换句话说, IF 或 ELSE 的 区块 内的声明无条件对声明和函数的闭括号之间的所有行生效. 同时还需注意当前还不支持声明动态变量, 例如 global Array%i%
.
对于创建 数组 的命令 (例如 StringSplit), 如果 假设全局模式 没有生效或数组的首个元素已经声明为局部变量时创建的数组是局部的 (如果函数的某个参数被传递时也是如此, 即使此参数为 ByRef 类型, 因为参数类似于局部变量). 相反地, 如果首个元素已经 声明为全局的, 那么创建全局的数组. 不过, 后面 混乱的常见根源 也适用于这些情况. StringSplit 创建的数组首个元素为 ArrayName0. 对于其他创建数组的命令, 例如 WinGet List, 首个元素为 ArrayName (即没有数字).
在函数中, 任何动态变量引用, 例如 Array%i%
, 总是解析为局部变量, 仅当这样名称的局部变量不存在而全局变量存在时才解析为全局变量. 如果两者都不存在并且需要创建此变量时, 当 假设全局模式 没有生效时它被创建为局部变量. 因此, 仅当函数被定义为 假设全局 函数时, 函数中才可以手动创建全局 数组 (通过使用类似 Array%i% := A_Index
的方法).
混乱的常见根源: 任何对 非 动态引用都会在脚本运行时创建那个变量. 例如, 在函数外使用时, MsgBox %Array1%
会在脚本运行的时候创建 Array1 为全局变量. 相反地, 在函数中使用 MsgBox %Array1%
会在脚本运行的时候创建 Array1 为函数的一个局部变量 (除非 假设全局模式 生效), 即使 Array 和 Array0 都声明为全局的.
动态调用函数
在 v1.0.47.06+, 通过百分号可以动态调用函数 (包括 内置函数). 例如,%Var%(x, "fox")
将调用名称保存在 Var 中的函数。同样地, Func%A_Index%()
将调用 Func1() 或 Func2() 等, 这取决于 A_Index 的当前值.
在 v1.1.07.00+, 在 %Var%()
中的 Var 可包含函数名, 函数引用 或 模仿函数的对象. 如果此函数不存在, 则调用 默认基对象的 __Call 元函数.
如果由于下面的某个原因无法调用函数, 计算包含调用的表达式时可能会过早静默停止, 这样可能会产生问题.
- 调用不存在的函数, 这可以通过使用
If IsFunc(VarContainingFuncName)
来避免. 除了 内置函数, 被调用函数的 定义 必须明确存在于脚本中, 通过类似 #Include 或对 库函数 的非动态调用的方法. - 传递过少的参数, 这可以通过检查 IsFunc() 的返回值来避免 (这是强制参数的数目加上一的数字). 注意: 在 v1.0.48+, 允许传递过多的参数; 每个额外的参数在被完全计算 (包括任何函数调用) 后丢弃.
最后, 对函数的动态调用比正常调用稍慢, 因为正常的调用在脚本开始运行前解析 (查询).
优化布尔求值
当在 表达式 中使用 AND, OR 和 三元运算符 时, 会对它们进行优化来提高性能 (不管当前是否存在函数调用). 通过不计算表达式中那些不影响最终结果的部分来进行优化运算. 为了说明这个概念, 请看这个例子:
if (ColorName <> "" AND not FindColor(ColorName)) MsgBox %ColorName% could not be found.
在上面的例子中, 如果 ColorName 变量为空则永远不会调用 FindColor() 函数. 这是由于 AND 的左侧结果将为 false, 因此其右边不可能让最终的结果为 true.
由于此特性, 所以需要注意到, 如果在 AND 或 OR 的右侧调用函数, 那么函数可能永远不会产生副作用 (例如改变全局变量的内容).
还需要注意在嵌套的 AND 和 OR 串联表达式的求值优化. 例如, 在后面的表达式中每当 ColorName 为空时只会进行最左边的比较. 这是因为此时最左边的比较已经足以确定最终的结果:
if (ColorName = "" OR FindColor(ColorName, Region1) OR FindColor(ColorName, Region2)) break ; 搜索内容为空或找到了匹配.
从上面的例子可以看出, 任何耗时的函数一般应该在 AND 或 OR 的右侧调用从而提高性能. 这种方法还能避免调用接受了一个不合适的值例如空字符串的函数.
在 v1.0.46+, 三元条件运算符 (?:) 也通过不计算丢弃的分支进行求值优化.
在函数中使用子程序
尽管在一个函数中不能 定义 其他函数, 但它可以含有子程序. 与其他子程序一样使用 Gosub 运行它们且使用 Return 返回 (此时 Return 属于 Gosub 而不是函数).
已知限制: 当前每个子程序 (标签) 的名称在整个脚本中必须是唯一的. 如果存在重复的标签, 在运行前程序会通知您.
如果函数使用 Gosub 跳转到公共子程序 (在函数外部的子程序), 那么所有外面的变量都是全局的而且在子程序返回前无法引用函数自身的 局部变量. 不过, A_ThisFunc 仍将包含正在执行的函数名称.
尽管不能使用 Goto 从函数中跳转到外面, 但可以在函数中使用 Gosub 到外部/公共的子程序, 然后在那里使用 Goto.
尽管一般不鼓励使用 Goto, 但它能用来在同个函数中的一个位置跳转到其他位置. 这样有助于简化包含许多个返回点而所有这些点在返回前需要进行一些清理的复杂函数.
函数可以包含能被外部调用的子程序,例如计时器、GUI g 标签和菜单项。通常把它们封装到一个单独的文件中供 #Include 使用, 这样避免了它们和脚本的 自动执行段 的冲突. 不过, 还存在下面这些限制:
- 如果它们所在的函数曾经被正常调用, 那么这样的子程序应该只使用 静态 和 全局 变量 (不使用 局部 变量). 这是由于中断函数调用线程的子程序 线程 (反之亦然) 可以改变被中断线程看到的局部变量的值. 此外, 在任何时候函数返回到其调用者时, 它包含的所有局部变量都被置空来释放占用的内存.
- 这样的子程序应该只使用 全局变量 (不使用 静态变量) 作为 GUI 控件的变量.
- 当一个子程序 线程 进入函数后, 在此线程中任何对 动态变量 的引用都被视为是 全局 的 (包括创建数组的那些命令).
Return, Exit 及一般说明
如果函数内的执行流在遇到 Return 前到达了函数的闭括号, 那么函数结束并返回空值 (空字符串) 给其调用者. 当函数明确省略 Return 的参数时也返回空值.
当函数使用 Exit 命令终止 当前线程 时, 其调用者不会接收到返回值. 例如,这个语句 Var := Add(2, 3)
中,如果 Add()
退出了那么 Var
会保持不变。如果函数出现了运行时错误, 例如 运行 了一个不存在的文件 (当 UseErrorLevel 选项不存在时), 也会出现同样的情况.
如果要返回一个额外的容易记住的值, 那么函数可以修改 ErrorLevel 的值来实现.
要使用一个或多个空值(空字符串)调用函数,可以使用空的引号对,例如:FindColor(ColorName, "")
。
因为调用函数不会开启新 线程, 所以函数对设置例如 SendMode 和 SetTitleMatchMode 做出的任何改变对其调用者同样有效.
函数的调用者可以传递不存在的变量或 数组 元素给它, 当函数期望和 ByRef 一致的参数时这很有用. 例如, 调用 GetNextLine(BlankArray%i%)
会自动地创建 局部 或全局的变量 BlankArray%i%
(是局部还是全局取决于调用者是否在函数内并且它的 假设全局模式 是否有效).
在函数中使用 ListVars 时, 它会显示函数的 局部变量 及其内容. 这样可以帮助调试脚本.
风格和命名约定
您也许会发现, 如果给复杂函数中的特定变量加上独特的前缀会让函数更容易阅读和维护. 例如, 使用 "p" 或 "p_" 开头来命名函数中的每个参数可以让它们的性质一目了然, 尤其是当函数中有许多 局部变量 吸引您的注意力的时候. 同样地, 前缀 "r" 或 "r_" 可以用于 ByRef 参数, 而 "s" 或 "s_" 可以用于 静态变量.
在定义函数时可以使用 One True Brace (OTB) 风格. 例如:
Add(x, y) { return x + y }
使用 #Include 在多个脚本间共享函数
可以使用 #Include 指令 (即使在脚本的顶部) 从外部文件中加载函数.
说明: 当脚本的执行流遇到函数定义时, 它会跳过函数 (使用一种瞬时的方法) 并在函数闭括号后恢复执行. 因此, 开始执行时执行流不会陷入函数中, 也不会因为在脚本的最顶部存在一个或多个函数而影响到 自动执行段.
函数库: 标准库和用户库 [v1.0.47+]
脚本中可以不需要通过 #Include 而调用外部文件中的函数. 要达到此目的, 一个和函数相同名称的文件必须存在于下面库目录的其中一个:
%A_ScriptDir%\Lib\ ; 局部库 - 需要 AHK_L 42+. %A_MyDocuments%\AutoHotkey\Lib\ ; 用户库. path-to-the-currently-running-AutoHotkey.exe\Lib\ ; 标准库.
例如,当脚本调用不存在的函数 MyFunc()
时,程序会在用户库中搜索名为“MyFunc.ahk”的文件。如果没有找到, 会继续在标准库中搜索此文件. 如果仍然没有找到匹配的文件且函数的名称含有下划线(例如 MyPrefix_MyFunc
),那么程序会在两个库中搜索名为 MyPrefix.ahk
的文件,如果找到则加载它。这样使得 MyPrefix.ahk
可以同时包含 MyPrefix_MyFunc
函数和其他名称以 MyPrefix_
开始的相关函数。
[AHK_L 42+]: 支持局部库且局部库优先于用户库和标准库.
虽然库文件通常只包含和它的文件名称相同的单个函数, 但它还可以包含仅被此函数调用的私有函数和子程序. 然而, 这样的函数应该使用相当特殊的名称, 因为它们仍属于全局命名空间; 也就是说, 可以在脚本的任意位置调用它们.
如果在库文件中使用 #Include, 那么 #Include 的工作目录为库文件自身所在的目录. 利用这个特性可以重定向到较大的库文件, 其中包含此函数及相关内容.
脚本编译器 (ahk2exe) 同样支持库函数. 不过, 它要求在编译器目录的上一级目录中存在 AutoHotkey.exe 的副本 (通常是这样). 如果不存在 AutoHotkey.exe, 编译器仍可以运行但无法自动包含库函数.
包含在库中的函数和其他函数执行的一样好, 因为在脚本开始执行前已经预加载了它们.
内置函数
在内置函数参数列表末尾的任何可选参数可以完全省略. 例如, WinExist("Untitled - Notepad")
是有效的, 因为它的其他三个参数被认为是空的.
如果脚本定义了与内置函数同名的函数, 那么内置函数会被覆盖. 例如, 脚本会调用它自己定义的 WinExist() 函数来代替标准的那个. 然而, 这样脚本将无法调用原来的函数.
在 DLL 文件中的外部函数可以使用 DllCall() 调用.
常用函数
FileExist(FilePattern): 如果 FilePattern 不存在 (如果未指定绝对路径, 则假定 FilePattern 在 A_WorkingDir 中) 则返回空值 (空字符串). 否则, 它返回首个匹配的文件或文件夹的 属性字符串 ("RASHNDOCT" 的子集). 如果文件没有属性 (很少见), 则返回 "X". FilePattern 可以是文件或文件夹的准确名称或包含通配符 (* 或 ?). 由于空字符串被视为 "false", 所以总可以把函数的返回值作为准布尔值来使用. 例如, if FileExist("C:\My File.txt")
语句当文件存在时则为真, 否则为假. 同样地, if InStr(FileExist("C:\My Folder"), "D")
语句仅当文件存在 并且 为目录时才为真. 相关命令: IfExist 和 FileGetAttrib.
GetKeyState(KeyName [, "P" 或 "T"]):与 GetKeyState 命令(它在按下时返回 D 而弹起时返回 U)不同,此函数在按下时返回 true(1)而弹起时返回 false(0)。如果 KeyName 无效, 则返回空字符串. 请参阅 GetKeyState 了解其他返回值和用法.
InStr(Haystack, Needle [, CaseSensitive = false, StartingPos = 1, Occurrence = 1]): 返回字符串 Needle 在字符串 Haystack 中的出现位置. 与 StringGetPos 不同, 首个字符位置为 1; 这是由于 0 相当于 "false", 使其成为直观的 "未找到" 的提示. 如果参数 CaseSensitive 省略或为 false, 搜索不区分大小写 (不区分的模式取决于 StringCaseSense); 否则, 必须准确匹配大小写. 如果 StartingPos 省略, 那么使用默认值 1 (Haystack 的起始处). 否则, 从 Haystack 的第二个字符开始请指定 2, 从第三个开始为 3, 等等. 如果 StartingPos 超过了 Haystack 的长度, 则返回 0. 如果 StartingPos 为 0 或负数, 搜索将从末尾的偏移处开始反向进行 (从右往左). 不论 StartingPos 的值是多少, 返回的位置总是相对于 Haystack 的首个字符. 例如, "abc" 在 "123abc789" 中的位置总为 4. 在 Occurrence 中指定 2 来返回第二个匹配的位置, 3 为第三个, 等等. 相关项: RegExMatch(), IfInString 和 StringGetPos.
RegExMatch(Haystack, NeedleRegEx [, UnquotedOutputVar = "", StartingPos = 1]):判断字符串是否包含某个匹配模式(正则表达式)。请参阅 RegExMatch() 了解详情。
RegExReplace(Haystack, NeedleRegEx [, Replacement = "", OutputVarCount = "", Limit = -1, StartingPos = 1]):替换字符串中匹配模式(正则表达式)出现的地方。请参阅 RegExReplace() 了解详情。
SubStr(String, StartingPos [, Length]) [v1.0.46+]: 在 String 中从 StartingPos 开始向右复制不超过 Length 长度的子字符串 (如果 Length 省略, 则默认为 "所有字符"). 对于 StartingPos, 指定 1 从首个字符开始, 2 从第二个开始等 (如果 StartingPos 超过了 String 长度, 则返回空字符串). 如果 StartingPos 小于 1, 那么它被看成是从字符串末尾开始的偏移. 例如, 0 提取最后一个字符而 -1 提取最后两个字符 (但如果 StartingPos 超过了字符串的左端, 则提取又从首个字符开始). Length 是需提取字符的最大数目 (当字符串剩余部分太短时提取的数目会比最大值少). 指定负的 Length 从而在返回字符串的末尾省略这个数目的字符(如果省略了全部或过多字符则返回空字符串)。相关项: RegExMatch(), StringMid, StringLeft/Right, StringTrimLeft/Right.
StrLen(String): 返回 String 的长度. 请参阅 StrLen() 了解详情。
StrSplit(String [, Delimiters, OmitChars]) [v1.1.13+]:使用指定的分隔符把一个字符串分解成多个子字符串并保存到数组中。请参阅StrSplit()了解详情。
WinActive([WinTitle, WinText, ExcludeTitle, ExcludeText]):如果活动窗口匹配指定的条件,则返回其 唯一 ID (HWND)。请参阅 WinActive() 了解详情。
WinExist([WinTitle, WinText, ExcludeTitle, ExcludeText]):返回首个匹配窗口的唯一 ID(HWND)。请参阅 WinExist() 了解详情。
杂项函数
Asc(String): 返回 String 中首个字符的字符编码 (介于 1 和 255 (在 ANSI 版本中) 或 65535 (在 Unicode 版本中) 的数字). 如果 String 为空, 则返回 0.
Chr(Number): 返回与 Number 表示的字符编码相对应的单个字符. 如果 Number 不在有效的字符编码范围内, 则返回空字符串. 常用的字符编码包括 9 (tab), 10 (换行), 13 (回车), 32 (空格), 48-57 (数字 0-9), 65-90 (大写字母 A-Z) 和 97-122 (小写字母 a-z).
DllCall():调用 DLL 文件中的函数,例如标准的 Windows API 函数。请参阅 DllCall() 了解详情。
FileOpen(): 提供面向对象的文件 I/O. 请参阅 FileOpen() 了解详情。
Func(FunctionName) [v1.1.00+]: 如果 FunctionName 没有显式出现在脚本中 (通过例如 #Include 或对 库函数 的非动态调用的方法), 则 Func() 返回 0. 否则, 它返回 到函数的引用. 这可以用来调用函数或获取类似参数的最小和最大数目的 信息.
GetKeyName(Key), GetKeyVK(Key), GetKeySC(Key) [v1.1.01+]: 获取按键的名称/文本, 虚拟按键码或扫描码. Key 可以为 VK 或 SC 码, 例如 "vkA2" 或 "sc01D", 以及两者的组合或键名. 例如, GetKeyName("vk1B")
和 GetKeyName("Esc")
都返回 "Escape", 而 GetKeyVK("Esc")
返回 27.
IsByRef(Var) [v1.1.01+]: 当 Var 为 ByRef 参数且调用者提供了变量时返回 1; 否则当 Var 为其他类型的变量时返回 0.
IsFunc(FunctionName) [v1.0.48+]: 如果 FunctionName 不是显式出现在脚本中 (通过例如 #Include 或对 库函数 的非动态调用的方法), 则 IsFunc() 返回 0. 否则它返回函数的强制参数个数加一的数字(例如 1 为函数可以不需要参数,2 为函数需要 1 个参数等等)。例如, 函数存在时 if IsFunc("MyFunc")
和 if IsFunc(VarContainingFunctionName)
语句为真, 否则为假. 在 v1.1.00+, FunctionName 为可以代替函数名的函数引用. 另请参阅: 动态函数调用, A_ThisFunc
IsLabel(LabelName):如果 LabelName 为脚本中的子程序、热键或热字串(不包括 LabelName 中末尾的冒号),则返回非零数字。例如, 标签存在时 if IsLabel(VarContainingLabelName)
将为真, 否则为假. 在类似 Gosub, Hotkey, Menu 和 Gui 命令中指定动态标签时, 使用此函数可以避免运行时错误. 另请参阅: Labels.
IsObject() [AHK_L 31+]: 判断某个值是否为对象. 另请参阅: 对象.
ListView 和 TreeView 函数:请参阅 ListView 和 TreeView 页面了解详情。
NumGet(VarOrAddress [, Offset = 0][, Type = "UPtr"]):返回在指定地址+偏移的位置存储的二进制数。请参阅 NumGet 了解详情。
NumPut(Number, VarOrAddress [, Offset = 0][, Type = "UPtr"]):以二进制格式把数字存储到指定地址+偏移的位置。请参阅 NumPut 了解详情。
OnMessage(MsgNumber [, "FunctionName"]): 监视消息/事件. 请参阅 OnMessage() 了解详情.
StrGet(Address [, Length] [, Encoding = None ] ) [AHK_L 46+]: 从内存地址复制字符串, 并可以对其在不同代码页间进行转换. 请参阅 StrGet() 了解详情.
StrPut(String, Address [, Length] [, Encoding = None ] ) [AHK_L 46+]: 复制字符串到内存地址, 并可以对其在不同代码页间进行转换. 请参阅 StrPut() 了解详情.
RegisterCallback():创建机器码地址,当它被调用时会重定向到脚本中的函数。请参阅 RegisterCallback() 了解详情。
Trim() [AHK_L 31+]:移除字符串的开始和/或末尾的某些字符。请参阅 Trim() 了解详情。
VarSetCapacity(UnquotedVarName [, RequestedCapacity, FillByte]): 增加或清空变量占用的内存. 请参阅 VarSetCapacity() 了解详情.
通用数学函数
注意: 如果某个传入参数为非数值时, 那么数学函数通常返回空值 (空字符串).
Abs(Number): 返回 Number 的绝对值. 返回值的类型与 Number 相同 (整数或浮点数).
Ceil(Number):返回由 Number 向上取整到最近的整数(不带 .00 后缀)。例如,Ceil(1.2)
结果为 2 而 Ceil(-1.2)
结果为 -1。
Exp(N): 返回 e (其近似值为 2.71828182845905) 的 N 次幂. N 可以为负数, 也可以为小数. 要计算 e 以外的其他数的次幂, 请使用 ** 运算符.
Floor(Number): 返回由 Number 向下取整到最近的整数 (不带任何 .00 后缀). 例如, Floor(1.2)
的结果为 1 而 Floor(-1.2)
结果为 -2.
Log(Number): 返回 Number 的对数 (底数为 10). 结果格式化为 浮点数. 如果 Number 为负数, 则返回空字符串.
Ln(Number): 返回 Number 的自然对数 (底数为 e). 结果格式化为 浮点数. 如果 Number 为负数, 则返回空字符串.
Mod(Dividend, Divisor): 求模. 返回被除数 Dividend 除以除数 Divisor 后得到的余数. 结果的正负总是与被除数一致. 例如,mod(5, 3)
和 mod(5, -3)
的结果都为 2,但 mod(-5, 3)
和 mod(-5, -3)
的结果为 -2。如果其中一个参数为浮点数, 那么结果也是浮点数. 例如, mod(5.0, 3)
的结果为 2.0 而 mod(5, 3.5)
的结果为 1.5. 如果除数为零, 则函数返回空值 (空字符串).
Round(Number [, N]): 如果 N 省略或为 0, 则 Number 被取整到最近的整数. 如果 N 为正数, 则 Number 被取整到 N 个小数位. 如果 N 为负数,则 Number 被取整到十进制小数点左边 N 位。例如,Round(345, -1)
的结果为 350 而 Round (345, -2)
的结果为 300。与 Transform Round 不同, 每当 N 省略或小于 1 时结果不带 .000 后缀. 在 v1.0.44.01+,N 值大于零时会准确显示 N 个小数位而不遵循 SetFormat 的设置。要避免这种情况, 需要对 Round() 的返回值再进行一次数学运算; 例如: Round(3.333, 1)+0
.
Sqrt(Number): 返回 Number 的平方根. 结果格式化为 浮点数. 如果 Number 为负数, 则函数返回空字值 (空字符串).
三角函数
Sin(Number) | Cos(Number) | Tan(Number):分别返回 Number 的正弦、余弦或三角函数值。Number 必须用弧度表示.
ASin(Number): 返回弧度表示的反正弦值 (其正弦值为 Number). 如果 Number 小于 -1 或大于 1, 则函数返回空值 (空字符串).
ACos(Number): 返回弧度表示的反余弦值 (其余弦值为 Number). 如果 Number 小于 -1 或大于 1, 则函数返回空值 (空字符串).
ATan(Number):返回弧度表示的反正切值(其正切值为 Number)。
注意: 要将弧度转换成角度, 请将其乘以 180/pi (约为 57.29578). 要将角度转换成弧度, 请将其乘以 pi/180 (约为 0.01745329252). pi 的值 (约为 3.141592653589793) 为 1 的反正切值的 4 倍.
其他函数
Polyethene 的命令函数:为每个有 OutputVar 的 AutoHotkey 命令提供了可调用的函数。可以通过 #Include 把这个库包含在任何脚本中.