在 Go 开发里,字符串写法看似只是符号区别,但一旦遇到 SQL、JSON、正则或多行帮助文本,双引号里的转义符就会迅速把代码可读性拉低。本文把双引号与反引号放到同一套判断标准里比较:它们各自会不会转义、能不能跨行、是否保留格式,以及哪些场景更适合直接改用反引号。看完之后,你可以更快判断一段字符串该怎么写,而不是在转义和拼接之间来回试错。
双引号和反引号,区别到底在哪里
Go 中的字符串字面量主要有两种写法:双引号字符串,以及反引号字符串,也就是 Raw String Literal。

它们最核心的差别,不在“长得不一样”,而在于 Go 编译器如何解释其中的内容。
- 双引号字符串会处理转义字符,比如
\n、\t、\"。 - 反引号字符串会按原样保留内容,基本可以理解为“写进去是什么,得到的就是什么”。
先看一个最直观的例子:
package main
import "fmt"
func main() {
// 双引号字符串:需要转义
str1 := "HellonWorldtGo"
// 反引号字符串:原样输出
str2 := `HellonWorldtGo`
fmt.Println("双引号:", str1)
fmt.Println("反引号:", str2)
}
这段代码要表达的重点是:双引号里的转义序列会被解释,而反引号里的内容不会。如果字符串里本来就有大量反斜杠、引号或换行,反引号通常更省事。
因此,判断标准可以先记一条:如果你需要“语义化的转义”,用双引号;如果你需要“字面量原样保留”,优先考虑反引号。
反引号为什么更适合复杂文本
反引号之所以在 Go 里很实用,主要来自三个特性。
原样保留,不解释转义
这是反引号最常见的使用原因。路径、正则、模板片段这类内容,往往会包含大量反斜杠和引号。用双引号时,代码里会塞进很多额外的转义;换成反引号,内容本身就能直接看懂。

// 双引号:需要转义反斜杠
path1 := "C:\Users\Admin\Documents"
// 反引号:直接书写
path2 := `C:UsersAdminDocuments`
这里需要补充一个容易忽略的规范细节:根据 Go 语言规范,回车符 '\r' 在反引号字符串中会被丢弃。实际影响主要出现在跨平台换行上,例如 Windows 常见的是 \r\n,而 Go 在原生字符串里会去掉其中的 \r,保留 \n。这样做的结果是,多行原生字符串在不同平台上更容易得到一致表现。
天然支持多行
如果字符串本身就是多行文本,反引号几乎总比双引号更自然。你不需要写 + 拼接,也不需要在每一行后面补 \n。

// 使用反引号编写多行文本
query := `SELECT id, name, email
FROM users
WHERE status = 'active'
ORDER BY created_at DESC`
fmt.Println(query)
对于 SQL、帮助说明、配置片段来说,这种写法的优势很明显:代码排版和输出格式基本一致,后期维护时不用先“脑内还原”真实文本。
会保留缩进、空格和换行
反引号不仅支持多行,还会保留你写下的格式本身。这一点对模板和说明文本尤其有用,因为视觉结构就是内容的一部分。
但这也是一个双刃剑:你想保留的缩进会留下来,不想保留的缩进也一样会留下来。也就是说,反引号更适合“格式本来就有意义”的字符串。
哪些场景最适合直接用反引号
在实际项目里,反引号并不是什么冷门技巧,而是几类文本场景下的常规选择。
SQL 语句
多行 SQL 是最典型的场景之一。条件多、字段多、联表多的时候,双引号字符串通常会把可读性拖得很低;反引号能把 SQL 保持成接近原始查询的样子。
// 使用反引号编写SQL语句
sql := `SELECT u.id, u.name, o.order_no
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active'
AND o.created_at > '2024-01-01'`
fmt.Println(sql)
这种写法的好处不只是“少写几个反斜杠”,更重要的是后续排查字段、条件和缩进关系时更直观。
JSON 和 HTML 模板
如果只是临时拼一个 JSON 文本,或者在代码里放一段 HTML 模板,反引号通常也比双引号更清楚,因为内部的双引号不需要额外转义。
// 使用反引号编写JSON模板
jsonTemplate := `{
"name": "张三",
"age": 25,
"email": "zhangsan@example.com"
}`
fmt.Println(jsonTemplate)
从阅读角度看,这段内容接近最终输出结果。开发者看到的,不是被转义符打断后的字符串,而是结构完整的 JSON。
正则表达式
正则表达式和反斜杠几乎绑定在一起,所以它也是反引号的高频适用场景。原因很简单:少一层转义,就少一层理解成本。
package main
import (
"fmt"
"regexp"
)
func main() {
// 使用反引号编写正则表达式
pattern := `^d{4}-d{2}-d{2}$`
re := regexp.MustCompile(pattern)
matched := re.MatchString("2024-01-15")
fmt.Println("匹配结果:", matched)
}
如果使用双引号,同样的表达式通常要写成 ^\\d{4}-\\d{2}-\\d{2}$。规则没变,但阅读门槛明显更高。对于经常需要回头修改模式的代码,这种差异很实际。
帮助文本、错误提示和多行说明
当一段输出内容本身就有段落、列表和缩进时,反引号可以让代码和最终展示几乎一一对应。
// 使用反引号编写帮助文档
helpText := `使用方法: myapp [选项] 参数
选项:
-h, --help 显示帮助信息
-v, --version 显示版本信息
-c, --config 指定配置文件路径
示例:
myapp -c config.yaml input.txt`
fmt.Println(helpText)
这类场景里,反引号最大的价值不是“语法更酷”,而是减少维护时的心智负担。你看到的格式,就是用户最终看到的格式。
使用反引号时,几个边界别忽略
反引号并不意味着可以替代所有双引号字符串。它很好用,但边界也很明确。
反引号字符串里不能直接放反引号
这是最硬的一条限制。因为反引号本身就是界定符,所以字符串内部不能直接再出现同样的符号。
// 错误:反引号中不能直接包含反引号
// str := `这是一个反引号:` `
// 正确:使用字符串拼接
str := "这是一个反引号:`"
fmt.Println(str)
遇到这种需求时,通常就老老实实回到双引号,或者做字符串拼接,不要强行把所有内容都塞进原生字符串。
缩进会被完整保留
反引号很适合保留格式,但如果你在代码里为了排版把内容缩进了,输出时这些空白也会保留下来。
// 注意:反引号会保留缩进
func example() {
text := `第一行
第二行(有缩进)
第三行(无缩进)`
fmt.Println(text)
}
这意味着在函数体内写多行原生字符串时,要特别注意首行和后续各行的对齐方式。若不希望带出这些缩进,可以把字符串顶格放置,或者后续再用字符串处理函数做清理。
性能不是选择依据
在性能层面,反引号字符串和双引号字符串在编译期的处理并没有显著差异。实际开发里,选择哪一种,主要还是看可读性、维护成本以及字符串内容本身的结构。
换句话说,不必把它理解成优化技巧。它首先是表达方式的选择,其次才是编码习惯。
实际开发里,什么时候用双引号,什么时候用反引号
如果要把选择规则压缩成几条可执行的经验,可以这样判断:
- 普通短字符串、需要转义字符时,用双引号。
- 包含大量反斜杠、引号或多行结构的文本,优先用反引号。
- SQL、模板、正则、帮助文档这类“格式本身就是内容”的文本,反引号通常更合适。
- 字符串内部必须出现反引号字符时,回到双引号或拼接方案。
本质上,这不是语法偏好问题,而是表达意图的问题。双引号适合“需要解释”的字符串,反引号适合“需要保留原样”的字符串。把这个分界线想清楚,Go 里的字符串处理就会顺很多。
对于简单文本,双引号依然是默认选项;但只要你发现自己开始和一堆 \n、\t、\\ 或多行拼接纠缠,通常就该考虑把它改写成反引号字符串了。









