# 热字符串和自动替换

注意：热字符串需要 Windows NT/2000/XP 或更高版本的支持。（译注：关于中文支持，请看本页末尾部分。）

## 介绍和简单的例子

虽然热字符串主要被用来在你输入它们的时候扩展缩写(自动替换)，但它们也可以用来启动任何脚本化的动作。在这方面它们与热键很相似，只不过它们一般由多个字符组成(也就是字符串)。

要定义一个热字符串，只需要把触发缩写放在两个冒号之间即可，像这样：

    ::btw::by the way

上例中，当你输入缩写 btw 时，就会被自动地替换成 "by the way"(不过默认情况下，你必须在输入 btw 之后再输入一个结束字符，比如一个空格、句点或者回车)。

因为输入的文本被自动地清除并被替换为第二个双冒号后面指定的字符串，所以上面的 "by the way" 例子被称为一个自动替换的热字符串。相比起来，热字符串也能被定义成执行任何自定义的动作，如下所示。注意命令必须在热字符串的下面：

    ::btw::
    MsgBox 你输入了 "btw"。
    return

    :*:]d::  ;这个热字符串通过下面的命令，把 "]d" 替换成了当前的日期和时间。
    FormatTime, CurrentDateTime,, M/d/yyyy h:mm tt  ;看起来将是 9/1/2005 3:53 PM 这样
    SendInput %CurrentDateTime% ;译注：输出的字符串末尾的 AM 或 PM 在中文操作系统下会用“上午”或“下午”来代替。但这两个中文字符会输出为乱码。关于中文支持，请看文末。
    return

尽管上面的两个例子不是自动替换的热字符串，但默认情况下，你输入的缩写也将被清除。这是通过自动地退格来实现的，但可以通过 b0 选项来禁用。

## 结束字符

除非星号选项已经生效，要不然你必须在一个热字符串的缩写后面输入一个结束字符才能触发它。结束字符初始由下列字符组成：-()[]{}':;"/\,.?!`n `t (注意 `n 是回车，`t 是 Tab，在 `n 和 `t 之间有个空格)。可以通过编辑下例来更改这组字符，这将为所有的热字符串设置结束字符，而不只是影响此指令下面的热字符串：

    #Hotstring EndChars -()[]{}:;'"/\,.?!`n `t

## 选项

可以使用以下两种方式改变热字符串的默认行为：

1. #Hotstring 指令，可以影响脚本中它那个点下面的所有热字符串。下例将使 C 和 R 选项生效：
   #Hotstring c r
2. 把选项放在第一个双冒号之间。下例使 C 和 * 选项在当前热字符串中生效：
   :c*:j@::john@somedomain.com ;区分大小写并且“不需要使用结束字符”。

下面列出了每个选项的描述。当用上面的方法指定多个选项时，可以在它们之间选择性地包含空格。（译注：我会将下列字母选项对应的英文关键词用绿色标注，以帮助记忆！）
* (星号): 不需要结束字符(比如空格、句点或回车)来触发热字符串。例如：

    :*:j@::jsmith@somedomain.com

上例中，当你一输入 @ 字符时，它就会送出它的替换字串。如果在 #Hotstring 指令中使用了它，可以用 *0 来关闭这个选项。

? (问号): 就算热字符串是在另一个词里，也可以被触发；就是说，在字符串被混排前立即输入字符。例如，如果 :?:al::airline 是一个热字符串，输入 "practial " 时将会产生 "practiairline "（译注：热字符串的缩写必须在另一个词的结尾）。可用 ?0 关闭此选项。

B0 (B 后跟一个零。Backspace 0): 不使用自动退格来清除你输入的缩写。之后可以使用一个 B 再启用之前被关掉的退格功能。脚本也可以通过 {bs 5} 发送 5 个退格键来自行退格。类似地，能通过 {left 5} 发送左方向键击。例如，下面的热字符串制造出 <em></em> 并把光标向左移动 5 个位置(因此它处在标签之间了)：

    :*b0:<em>::</em>{left 5}

C (Case sensitive): 区分大小写：当你输入缩写的时候，它必须和脚本中定义的大小写严格匹配。可用 C0 来关闭大小写敏感性。

C1: 不遵守输入的大小写。使用此选项可以让自动替换热字符串不区分大小写并防止热字符串遵守你实际输入的字符的大小写。如果你输入了全部大写的缩写，遵守大小写的热字符串(这是默认的)将用全部大写的方式制造它们的替换文本。如果你只输入了首个大写的字母，替换字串的首个字母(如果是字母的话)也将是大写的。如果你以任何其它方式输入大小写，替换字串将只严格地按脚本定义的那样发送。当使用 #Hotstring 指令时，C0 可被用来关闭此选项，这能使热字符串重新遵守大小写。

Kn (Key-delay n): 按键延迟：这个罕用的选项用来设置自动退格或者自动替换产生的键击之间的延迟。给 n 设定新的延迟；例如，指定 k10 将产生 10 毫秒的延迟，k-1 则没有延迟。本选项实际的表现取决于当前生效的发送模式是哪个：

- SI (SendInput): 因为这种模式是没有延迟的，所以按键延迟将被忽略。在 SendInput 模式无效的情况下例外，这时热字符串转而采用到下面的 SendPlay 模式(它会遵从按键延迟)。
- SP (SendPlay): 默认延迟为零，与 SendPlay 模式下的 -1(无延迟) 相同。这种模式下，延迟实际上是一个按键时长，而非键击之间的延迟。
- SE (SendEvent): 默认延迟为零。多数情况下建议值为零，因为它快速并且与其它进程配合得较好(由于内部会做一个 Sleep 0)。指定 k-1 彻底禁止延迟，如果你的 CPU 经常高负载运作，让自动替换执行的更快这样会有所帮助。当设为 -1 时，脚本的进程优先级就成为击键发送速度的重要因素。要提高脚本的优先级，请用 Process, Priority,, High。

O (Omit): 在生成替换时，省略掉自动替换热字符串的结束字符。这在你仍需要一个结束字符来明确一个热字符串，但实际上又不想让结束字符显示在屏幕上的时候会很有用。例如，假设 :o:ar::aristocrat 是个热字符串，在输入 "ar" 后跟空格时将会生成末尾不带空格的 "aristocrat"，这就使你在输入一个单词的复数或所有格时，不用再退格了。可用 O0 (字母 O 后跟一个零)关闭此选项。

Pn (Priority n): 热字符串的优先级(比如 P1)。这个罕用的选项对自动替换热字符串无效。

R (Raw): 按照原样发送替换文本；就是说，完全按它显示的那样，而不将 {Enter} 转换为一个 ENTER 键击，也不把 ^c 转换为 Control-C，等等。对有连续部分的热字符串，此选项会自动生效。可用 R0 关闭此选项。

S I 或 SP 或 SE [1.0.43 及之后版本]: 设定自动替换热字符串发送它们键击的方法。这些选项互斥：每次仅有一个会生效。每个选项描述如下：

- SI 代表 SendInput，由于优越的速度和可靠性，它在 1.0.43 及之后版本成为默认项。另一个好处是和下面的 SendPlay 一样，SendInput 在热字符串自动替换文本时，会延迟你输入的任何内容。这能防止替换文本被你的键击穿插。当 SendInput 无效时，热字符串将自动使用 SendPlay 来代替。
- SP 代表 SendPlay，它能使热字符串用在很多类型的游戏中。
- SE 代表 SendEvent，在 1.0.43 之前的版本中它是默认项。

Z: 这个罕用的选项将在热字符串每次触发后重置热字符串识别器。换句话说，脚本将开始等待一个全新的热字符串，不考虑你之前输入的任何内容。这能避免不必要的热字符串触发。要理解它，请看下面的热字符串：

    :b0*?:11::
    SendInput xx
    return

因为上面没有 Z 选项，在输入 111(三个连续的 1)时将会触发热字符串两次，因为中间的 1 既是首次触发的末尾字符，又是第二次触发的开头字符。通过在 b0 前面添加字母 Z，你就要输入四个 1 来代替三个 1，从而触发两次热字符串。可用 Z0 关闭此选项。

## 超长替换

使用一个连续部分可以让产生大量替换文本的热字符串变得更具可读性和可维护性。例如：

    ::text1:: ;译注：由于不能直接发送中文字符，故下面三行英文保留，只在每行下面通过注解形式翻译。
    (
    Any text between the top and bottom parentheses is treated literally, including commas and percent signs.
    ;在顶部和底部圆括号之间的任何文本都作为原义来对待，包括逗号和百分号。
    By default, the hard carriage return (Enter) between the previous line and this one is also preserved.
    ;默认，上一行与本行之间的硬回车(Enter)也将被保留。
        By default, the indentation (tab) to the left of this line is preserved.
        ;默认，本行左侧的缩进(tab)将被保留。

    See continuation section for how to change these default behaviors.
    ;如何修改这些默认的特性，请看连续部分。
    )

连续部分的出现也会使热字符串默认使用 raw 模式。要改变这种特殊默认模式的唯一方法，就是给每个使用连续部分的热字符串指定 r0 选项(比如 :r0:text1::)。

## 上下文相关的热字符串

#IfWinActive/Exist 指令可以使选定的热字符串产生上下文相关性。根据激活或存在的窗口类型，这样的热字符串会发送不同的替换文本，执行不同的操作或者干脆无效。例如：

    #IfWinActive ahk_class Notepad
    ::btw::This replacement text will appear only in Notepad. ;这行替换文本将只出现在记事本中。
    #IfWinActive
    ::btw::This replacement text appears in windows other than Notepad. ;这行替换文本将出现在任何不是记事本的窗口中。

## 自动校正

下面这个脚本使用了热字符串来即时校正大约 4700 个常见的英文拼写错误。它还包括一个 Win+H 热键，可以方便地添加更多拼写错误：

下载: AutoCorrect.ahk (127 KB)

作者: Jim Biancolo 以及维基百科的常见拼写错误列表

## 备注

当前在替换文本中不支持 %MyVar% 这样的变量引用。解决方法是，不要让这样的热字符串自动替换，而是在缩写下面使用 SendInput 命令，和一个仅包含单词 Return 的行。

要在替换文本后发送一个额外的空格或 tab，可以把它们放在替换文本后面，但是要以一个重音符/反引号(`)作为结尾字符。例如：

    :*:btw::By the way `

鼠标左键或右键的任何点击都将重置热字符串的识别器。换句话说，脚本将开始等待一个全新的热字符串，不考虑你之前输入的任何内容(如果不想这样，可以在脚本的任意位置指定一行 #Hotstring NoMouse)。由于每次点击通常都会移动文本插入点(光标)或者把键盘的焦点置于一个新的控件/区域。所以这种情况下，通常需要：1) 触发热字符串，即使它没有问号选项；2) 防止你在意外地鼠标点击后输入的内容与你之前输入的那些形成一个有效的缩写从而触发。因此这种“点击后重新识别”的特性是默认的。

内置变量 A_EndChar 里包含了你用来触发最近的非自动替换型热字符串的结束字符。如果不需要使用结束字符(由于使用了 * 选项)，它将为空。在让热字符串使用 Send 命令或者那些会根据你输入的结束字符而变化的行为的时候，A_EndChar 变量将很有用。要发送结束字符它自身，可用 SendRaw %A_EndChar% (之所以使用 SendRaw，是因为普通的 Send 命令不能正确发送比如 !{} 这些字符)。

虽然在热字符串定义中的逗号、百分号和单冒号无需转义，但那些跟在空格或 tab 后面的反引号和分号是需要的。请看转义顺序中的完整列表。

虽然在自动替换型文本中支持 Send 命令的特殊字符比如 {Enter} (如果未使用 raw 选项的话)，但热字符串的缩写它们自身却不使用。而是为 ENTER 键指定 `n，为 TAB 指定 `t (或一个原义的 tab，请看转义顺序中的完整列表)。例如：当你输入 "ab" 后跟一个 tab 时，将触发热字符串 :*:ab`t::。

在热字符串定义中会原义地对待空格和 tab。例如，下面两行的结果是不同的：
::btw::by the way
::btw:: by the way

每个热字符串缩写的长度都不能超过 40 个字符。如果超出此长度，程序将会弹出警告对话框。而当发送模式是默认的 SendInput 时，热字符串的替换文本长度限制约为 5000 个字符。这个限制可以通过切换到其它发送模式而增加到 16,383 个字符。此外，在热字符串内使用 SendPlay %MyVariable% 则可以发送一个不限制数量的文本。

热字符串的定义顺序决定了它们彼此的优先级。换句话说，如果有多个热字符串可以匹配你输入的内容，那么只有脚本中首个列出的热字符串会起作用。相关主题：上下文相关的热字符串。

为了识别热字符串，你输入的所有退格都会被算进来。不过在编辑器中使用方向键, PageUp, PageDown, Home 以及 End 来导航的话，会导致热字符串识别过程被重置。换句话说，它会开始等待一个全新的热字符串。

即使激活窗口忽略你的键击时，也能输入热字符串。换句话说，就算触发的缩写不可见，热字符串也仍然会被触发。另外，你依然可以使用退格键来撤销最近输入的键击(尽管你看不见效果)。

将首对冒号(以及其中的任何选项符号)包含在热字符串名称的前面，可以 Gosub 或 Goto 到一个热字符串标签。例如：Gosub ::xyz。不过，跳转到一个单行(自动替换型)热字符串的话，将什么也不会做，只会执行一个 return。

虽然不会监视热字符串，在一个不可见的 Input 命令过程中也不会被触发，但是可见的 Input 却会触发它们。

任何 AutoHotkey 脚本产生的键击永远都不会触发热字符串。这避免了热字符串彼此反复触发而进入无限循环的可能性。

对于某些特定的目的，Input 命令会比热字符串更灵活。比如，它允许你的键击在激活窗口中不可见(比如一个游戏)。它还支持非字符型的结束按键，比如 Escape。

任何包含热字符串的脚本都会自动使用 keyboard hook 。

在下列情况中热字符串与热键具有相同的特性：

- 它们都会受到 Suspend 命令的影响。
- 它们都遵守 #MaxThreads 和 #MaxThreadsPerHotkey (但不遵守 #MaxThreadsBuffer)的设置。
- 包含热字符串的脚本将会自动地持续运行。
- 非自动替换型热字符串在启动时将会创建一个新的线程。另外，它们会更新内置的热键变量，比如 A_ThisHotkey。

已知限制：在 Java 应用程序的某些系统中，热字符串可能会干扰用户键入 diacritical 字母(通过 dead 键)。解决方法是，可以临时打开 Suspend (这会禁用所有的热字符串)。

## 热字符串助手

Andreas Borutta 推荐了下面的脚本，如果你是一个重度热字符串用户，它将会很有用。通过按 Win+H (或你自定义的其它热键)，当前选中的文本可以被转化成一个热字符串。例如，假设你在一个字处理软件中选择了 "by the way"，那么按下 Win+H 将会提示你输入一个缩写(比如 btw)，之后就会在脚本中添加新的热字符串。随后它会重载脚本来激活热字符串。

    #h::  ; Win+H 热键
    ;获取当前选中的文本。使用剪贴板代替 "ControlGet Selected"
    ;是因为它能在更多种类的编辑器(也就是字处理软件)中起作用。
    ;当前剪贴板中的内容将被保存，以便之后恢复。尽管只能恢复纯文本，但总比没有的好：
    AutoTrim Off  ;保留剪贴板中首尾的空白字符。
    ClipboardOld = %ClipboardAll%
    Clipboard =  ;为检验正确，需要清空。
    Send ^c
    ClipWait 1
    if ErrorLevel  ; ClipWait 超时
        return
    ;将回车换行和/或换行符替换为 `n 以便使用在一个 "send-raw" 的热字符串中：
    ;对任何在 raw 模式下可能出问题的其他字符应用同样的操作：
    StringReplace, Hotstring, Clipboard, ``, ````, All  ;为避免与下列替换冲突，先作这个替换。
    StringReplace, Hotstring, Hotstring, `r`n, ``r, All  ;在微软 Word 等等软件中 `r 比 `n 表现得更好。
    StringReplace, Hotstring, Hotstring, `n, ``r, All
    StringReplace, Hotstring, Hotstring, %A_Tab%, ``t, All
    StringReplace, Hotstring, Hotstring, `;, ```;, All
    Clipboard = %ClipboardOld%  ;恢复记事本之前的内容
    ;这将把输入对话框中的光标移动到更人性化的位置：
    SetTimer, MoveCaret, 10
    ;显示提供了缺省热字符串的输入对话框：
    InputBox, Hotstring, 新建热字符串, 在光标处输入你的缩写。需要的话，你也可以编辑替换文本。`n`n 示例::R:btw`::by the way,,,,,,,, :R:`::%Hotstring%
    if ErrorLevel  ;用户选择了取消
        return
    IfInString, Hotstring, :R`:::
    {
        MsgBox 你没有输入缩写。热字符串不会被添加。
        return
    }
    ;否则，添加热字符串并重新加载脚本：
    FileAppend, `n%Hotstring%, %A_ScriptFullPath%  ;在字符串开头放一个 `n 以防文件在它的末尾没有空行。
    Reload
    Sleep 200 ;如果重新加载成功，那么在 Sleep 期间就会关闭这个实例，这样就永远不会运行到下一行。
    MsgBox, 4,, 刚刚添加的热字符串格式化不正确。你要打开脚本编辑么？注意，有问题的热字符串在脚本的最后一行。
    IfMsgBox, Yes, Edit
    return

    MoveCaret:
    IfWinNotActive, New Hotstring
        return
    ;否则，将输入对话框中的光标移动到用户输入缩写的位置。
    Send {Home}{Right 3}
    SetTimer, MoveCaret, Off
    return

## 译注：关于中文支持

注意：这部分内容是译者自己添加的。

由于 AutoHotkey 不能直接发送中文字符，所以可以尝试将 Send 命令和其他方法组合使用，从而实现发送中文的目的。

热字符串支持缩写触发命令，所以只要把下面这一系列的命令写在缩写下面就可以实现热字符串发送中文了。

方法 1：把要发送的中文赋值给剪贴板，然后发送一个粘贴动作即可自动转换字符编码：

    ClipboardOld = %ClipboardAll% ;保留剪贴板中原来的内容
    Clipboard = 需要发送的中文内容
    Send ^v
    Clipboard = %ClipboardOld%  ;恢复剪贴板初始的内容
    Return

方法 2：Yonken 在他的博客里有写过如何在脚本中使用 Send 发送中文。他给出的方案不依赖剪贴板，直接转换字符编码，最后通过函数的形式来调用。感兴趣的朋友可以去看看。不过转换的速度会相对慢一点。

注意，热字符串的缩写还不能使用中文，因为中文输入法输出的字符还不能被热字符串的识别器识别，解决方法未知。

翻译：wanggang999 修正：天堂之门 menk33@163.com 2008年12月8日