HOWTO · C++
在 C++ 中声明多行字符串
本文演示了如何在 C++ 中声明一个多行字符串。
本页内容
当你需要处理较长的文本,或希望在源代码中保留排版格式时,多行字符串就很重要。C++ 没有专门的多行字符串类型,但提供了几种构建方式:相邻字符串字面量、原始字符串字面量、反斜杠-换行拼接以及预处理器字符串化。这些技术的主要区别在于,源代码中的换行是否会变成字符串值中的换行。
本指南将通过可运行的示例逐一介绍每种方法,并在最后进行比较,帮助你为代码选择合适的方案。
使用相邻字符串字面量构建多行字符串
当编译器看到两个或多个双引号字符串字面量彼此相邻、仅由空白字符分隔时,会在编译时把它们拼接成单个值。把一个字面量拆到多行源代码中纯粹是排版选择——除非你显式写出 \n,否则不会在值中添加换行;如果单词之间应保持分隔,也必须显式包含空格。
运行以下示例;代码中的字符串与输出按原样保留英文:
本节中的代码以及显示的标签或输出仍为英文。
#include <iostream>
#include <string>
int main() {
std::string message =
"I love"
" getting my doubts solved"
" by the help of Delft Stack Tutorials.";
std::cout << message << std::endl;
return 0;
}
I love getting my doubts solved by the help of Delft Stack Tutorials.
当目标是 const char *——指向该字面量的只读指针——而不是 std::string 对象时,相邻字面量拼接的工作方式完全相同;示例中的字符串与输出仍为英文:
#include <cstdio>
int main() {
const char *lines =
"Line 1 "
"Line 2 "
"Line 3";
std::printf("%s\n", lines);
return 0;
}
Line 1 Line 2 Line 3
如果文本会被修改或传给能够处理字符串的 API,请选择 std::string;对于只用于打印或检查的静态文本,const char * 更合适。
使用原始字符串字面量保留换行
C++11 引入了原始字符串字面量,使物理换行能够直接存在于字面量内部。R"( 与 )" 之间的所有内容——包括换行、缩进、引号和反斜杠——都会原样成为值的一部分,因此不需要转义序列;示例中的字符串与输出仍为英文:
#include <iostream>
#include <string>
int main() {
std::string page = R"(I love
getting my doubts solved
by the help of Delft Stack Tutorials.)";
std::cout << page << std::endl;
return 0;
}
I love
getting my doubts solved
by the help of Delft Stack Tutorials.
由于内容会被逐字复制,请注意两个细节:紧跟在起始 R"( 之后输入的换行会被存入值中,续行前的缩进也会被保留。如果文本中会出现 )" 序列,可以选择自定义分隔符,例如 R"tag(...)tag"——此时结束标记必须带有该 tag,因此文本中普通的 )" 不会造成影响。
使用反斜杠-换行拼接延续字符串字面量
如果反斜杠是源代码行的最后一个字符,它会在编译前把该行与下一行拼接起来。在字符串字面量内部,这能让字面量跨越多行源代码,但被拼接的换行会被移除——它不会在值中产生换行。这正是它与 \n 转义的关键区别,后者确实会添加换行;示例中的字符串与输出仍为英文:
#include <cstdio>
int main() {
const char *text =
"First line\n"
"Second line\n\
Third line\n";
std::printf("%s", text);
return 0;
}
First line
Second line
Third line
输出展示了两种机制的作用:每个 \n 都会写入一个换行,而反斜杠拼接只会连接源代码行——续行开头的前导空格会成为值中的真实字符。这种对空白的敏感性是主要隐患:为了可读性输入的缩进最终会进入字符串,而反斜杠后若有任何字符,即使只是一个空格,也会使拼接失效。只有当你能完全控制周边排版时才应使用拼接。
了解宏字符串化的局限
可变参数宏可以使用字符串化运算符把参数转换为字符串字面量,例如 #define MULTILINE_STRING(...) #__VA_ARGS__。传入分布在多行上的文本会得到一个带引号的字面量——但预处理器会把每一串空白字符(包括换行)规范化为一个空格;示例中的字符串与输出仍为英文:
#include <iostream>
#include <string>
#define MULTILINE_STRING(...) #__VA_ARGS__
int main() {
std::string message = MULTILINE_STRING(
I love getting my doubts solved by the help of Delft Stack Tutorials.);
std::cout << message << std::endl;
return 0;
}
I love getting my doubts solved by the help of Delft Stack Tutorials.
这使宏字符串化只是一种受限选项,而不是通用的多行字符串工具。它无法保留换行;参数文本会按预处理记号处理,而不是当作字符串内容处理(因此散落的标点可能产生出乎意料的结果);基于宏的代码也更难阅读和调试。只有当预处理器驱动的代码确实需要把记号转换为文本时,才应使用它。
选择合适的多行字符串技术
- 原始字符串字面量:当换行、缩进或
\、"等字符应原样保留在值中时,这是默认选择——非常适合嵌入文本块、JSON、SQL 或正则表达式。 - 相邻字符串字面量:在保持源代码行较短的同时精确控制值;每个空格和
\n都是显式的,因此不会混入任何意外内容。 - 反斜杠-换行拼接:较脆弱且对缩进敏感;主要存在于遗留代码和宏定义中,在宏定义里仍然需要用它让定义跨越多行。
- 宏字符串化:只适合确实需要把预处理记号转换为文本的、以预处理器为主的代码。
对于确实跨越多行的值,请使用原始字符串字面量。对于为了排版而在源代码中拆成多行的长单行值,相邻字面量最清晰——并且它与 std::string 或 const char * 都能自然搭配。