如何编写多行字符串字面量

554
有没有办法在C++中像Perl一样拥有多行纯文本的常量字面值? 也许可以通过解析技巧和#include引入文件来实现吗?
我知道在C++11中可以使用原始字符串来实现。

1
通常情况下,您不希望将字符串字面量嵌入到代码中。对于国际化和本地化,最好将字符串字面量放入在运行时加载的配置文件中。 - Martin York
58
在代码中放置字符串字面量并不总是一个问题:如果该字符串不用于向用户表示信息,例如:SQL语句、文件名、注册表键名、要执行的命令行等。 - mmmmmmmm
3
@Martin:然而,了解这个仍然是有用的。例如,我曾经使用它来分解复杂的正则表达式。 - Boojum
10个回答

746
嗯...差不多吧。最简单的方法就是利用编译器将相邻的字符串字面量连接起来的特性。
const char *text =
  "This text is pretty long, but will be "
  "concatenated into just a single string. "
  "The disadvantage is that you have to quote "
  "each part, and newlines must be literal as "
  "usual.";

缩进并不重要,因为它不在引号内。
只要你小心转义嵌入的换行符,你也可以这样做。如果不这样做,就像我的第一个答案一样,将无法编译。
const char *text2 =
  "Here, on the other hand, I've gone crazy \
and really let the literal span several lines, \
without bothering with quoting each line's \
content. This works, but you can't indent.";

再次注意每行末尾的反斜杠,它们必须紧跟在行结束之前,它们是用来转义源代码中的换行符,使得所有内容都像没有换行符一样处理。在字符串中,你不会在反斜杠的位置得到换行符。使用这种形式,显然无法对文本进行缩进,因为缩进将成为字符串的一部分,导致随机出现空格,使其变得混乱不堪。

3
过去有人告诉我第一种选择可能需要实现,但是我还没有找到任何不支持这种语法的编译器。 - Jason Mock
35
@Jason: 它不一定是C89之前编译器的一部分,但它在C89中被定义,因此基本上在所有地方都得到了支持。 - Jonathan Leffler
4
如果你确实想在C++98中将字符串格式化为多行,只需在每个引用字符串片段的结束空格处使用\n进行替换。C++11原始字符串字面值仍然是我最喜欢的。 - emsr
3
请注意,源代码行末的换行符不会包含在字符串中,而是被跳过。如果您想让换行符成为字符串的一部分,您需要在行末添加 \n\。 - hyde
2
Microsoft Visual Studio 中有一个很恶心的 bug。如果在行末使用反斜杠,则它会自动缩进字符串内部的文本。 - palota
显示剩余3条评论

599

C++11引入了原始字符串字面量,类似于Shell、Python、Perl和Ruby等脚本语言中的here-doc。

const char * vogon_poem = R"V0G0N(
             O freddled gruntbuggly thy micturations are to me
                 As plured gabbleblochits on a lurgid bee.
              Groop, I implore thee my foonting turlingdromes.   
           And hooptiously drangle me with crinkly bindlewurdles,
Or I will rend thee in the gobberwarts with my blurlecruncheon, see if I don't.

                (by Prostetnic Vogon Jeltz; see p. 56/57)
)V0G0N";

这个字符串中所有的空格、缩进和换行符都被保留。

它们也可以是 utf-8|16|32 或 wchar_t(使用通常的前缀)。

我应该指出,转义序列 V0G0N 实际上在这里并不需要。其存在可以允许在字符串中放置 )”,换句话说,我本可以写成

                "(by Prostetnic Vogon Jeltz; see p. 56/57)"

(请注意多余的引号),即使字符串中有多余的引号,上述字符串仍然是正确的。否则,我可能只能使用:

const char * vogon_poem = R"( ... )";

引号内部的括号仍然是必需的。


35
这真的是我想要的,即能够避免引号、反斜杠-N、转义等,但仍然使换行符出现在实际的字符串中。这对于嵌入式代码(例如着色器或Lua)非常方便。不幸的是,我们并不都使用C++-0x。 :-( - mlepage
3
我更习惯使用clang和gcc编译器。在这些编译器中,你需要设置一个标志来支持C++0x或c++11。看到微软的网站上似乎还没有原始字面量。我了解到随着C++特性的实现,微软将更快地发布新的编译器更新。要获取最新版本,请查看Visual C++ Compiler November 2012 CTP[http://www.microsoft.com/en-us/download/details.aspx?id=35515]。 - emsr
5
只需使用 #if 0#endif 来注释掉代码块,嵌套也可以。 - bobbogo
2
受沃冈诗歌的启发! - Thane Plummer
6
我的多行字符串总是使用 V0G0N,因为我无法记住这个语法,所以我总是查找答案,然后感到很有趣。 - bhaller
显示剩余12条评论

66

您也可以这样做:

const char *longString = R""""(
This is 
a very 
long 
string
)"""";

2
谢谢,这很棒,甚至在C中也可以工作。显然,char longString[] = R""""( This is a very long string )""""; 对我也适用。 - struggling_learner
3
这个函数会在字符串开头和结尾加上一个换行符吗? - Tim MB
2
这是一个原始字符串字面量,自C++11起可用。 - Mikolasan
1
可以与Arduino一起使用!现在我可以轻松地提供嵌入式网页服务了! - nmz787
一个很好的变化是在@emsr的答案基础上,可以使其看起来不那么像PERL,而更像Python。 - automorphic
但是当字符串文字包含字符串内的 ( ...) 时怎么办?我试图将其转换为 R 风格,这样就不需要 \" 等符号,但无法让它正常工作:L"<xsl:variable name=\"DutyHistory\" select=\"document('DutyAssignHistory.XML')\"/>" - Andrew Truckle

32

#define MULTILINE(...) #__VA_ARGS__
消费括号内的所有内容。
将任意数量连续的空格字符替换为单个空格。


1
如果需要换行,您可以添加\n - Simon
请注意,\(因此也包括\n)会被直接复制,但是"会被转换为\"。因此,MULTILINE(1, "2" \3)将生成"1, \"2\" \3" - Andreas Spindler
@AndreasSpindler 引号和反斜杠都需要通过(额外的)反斜杠进行转义,只要它们出现在字符串或字符字面标记内部。不确定您想表达什么。存在未匹配的引号(双引号或单引号)是非法的,因此缩略词无效,或者无论如何都是奇数个,这可能是最大的缺点。无论如何都要加1。 "真正的程序员"总是成对使用缩略词,没有换行符干扰,以便单引号保持平衡。 - Potatoswatter
重点是他写了“消耗括号之间的所有内容”。 - Andreas Spindler

31

使用宏的一种可能方便的方式是输入多行字符串。这仅在引号和括号平衡并且不包含“顶级”逗号时才有效:

#define MULTI_LINE_STRING(a) #a
const char *text = MULTI_LINE_STRING(
  Using this trick(,) you don't need to use quotes.
  Though newlines and     multiple     white   spaces
  will be replaced by a single whitespace.
);
printf("[[%s]]\n",text);

使用gcc 4.6或g++ 4.6编译,将会生成:[[ 使用此技巧,您无需使用引号。尽管换行和多个空格将被替换为单个空格。]]

请注意,不能出现在字符串中,除非它包含在括号或引号中。单引号是可能的,但会创建编译器警告。

编辑:如评论中所述,#define MULTI_LINE_STRING(...) #__VA_ARGS__允许使用,


为了在C++项目中包含一些lua代码片段,我最终编写了一个小的Python脚本。在这个脚本中,我输入了多行字符串,然后让它生成一个C++源代码文件。 - bcmpinc
对我来说非常完美,从Collada文件中添加一个巨大的多行浮点列表字符串进行单元测试。我不想到处加引号,我需要一个复制粘贴的解决方案。 - Soylent Graham
9
如果您希望字符串包含逗号,可以使用#define MULTILINE(...) #__VA_ARGS__ - Simon
2
请注意,此操作将删除大部分额外的空格(包括所有\n\r),这对某些情况可能很方便,但对其他情况可能是致命的。 - BCS

29
你可以直接这样做:
const char *text = "This is my string it is "
     "very long";

它与@unwind的答案有何不同? - Sisir
21
@Sisir,我在Unwind之前发了这条消息,相差2分钟。 - Eric

15

仅对@emsr在@unwind的回答中的评论进行一些阐述,如果一个人没有足够幸运拥有C++11编译器(比如GCC 4.2.1),并且想要将换行符嵌入字符串(无论是char *还是string类),可以编写类似于以下代码:

const char *text =
  "This text is pretty long, but will be\n"
  "concatenated into just a single string.\n"
  "The disadvantage is that you have to quote\n"
  "each part, and newlines must be literal as\n"
  "usual.";

很明显,很真实,但是@emsr的简短评论第一次阅读时没有给我留下深刻印象,所以我不得不自己发现这一点。希望我能为其他人节省几分钟时间。


12

既然一盎司经验胜过吨的理论,我为MULTILINE尝试了一个小测试程序:

#define MULTILINE(...) #__VA_ARGS__

const char *mstr[] =
{
    MULTILINE(1, 2, 3),       // "1, 2, 3"
    MULTILINE(1,2,3),         // "1,2,3"
    MULTILINE(1 , 2 , 3),     // "1 , 2 , 3"
    MULTILINE( 1 , 2 , 3 ),   // "1 , 2 , 3"
    MULTILINE((1,  2,  3)),   // "(1,  2,  3)"
    MULTILINE(1
              2
              3),             // "1 2 3"
    MULTILINE(1\n2\n3\n),     // "1\n2\n3\n"
    MULTILINE(1\n
              2\n
              3\n),           // "1\n 2\n 3\n"
    MULTILINE(1, "2" \3)      // "1, \"2\" \3"
};

使用cpp -P -std=c++11 文件名编译此片段以进行复现。

#__VA_ARGS__背后的技巧在于__VA_ARGS__不会处理逗号分隔符。因此,您可以将其传递给字符串化运算符。前导和尾随空格被修剪,并且单词之间的空格(包括换行符)被压缩为一个空格。圆括号需要平衡。我认为这些缺点解释了为什么C++11的设计者们,尽管有了#__VA_ARGS__,还看到了原始字符串字面量的需求。


9
// C++11. 
std::string index_html=R"html(
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>VIPSDK MONITOR</title>
    <meta http-equiv="refresh" content="10">
</head>
<style type="text/css">
</style>
</html>
)html";

18
请在您的答案中添加解释,而不仅仅是代码片段。 - Geordie

-7

选项1. 使用Boost库,你可以如下声明字符串:

const boost::string_view helpText = "This is very long help text.\n"
      "Also more text is here\n"
      "And here\n"

// Pass help text here
setHelpText(helpText);

选项2。如果您的项目中没有Boost可用,您可以在现代C++中使用std::string_view()


这是C++语言的一个特性,与boost无关。 - sstn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接