什么是原始字符串?

60

我在C++17草案n4713中发现了这段代码片段:

#define R "x"
const char* s = R"y"; // ill-formed raw string, not "x" "y"

"原始字符串"是什么?它有什么作用?

4个回答

78

原始字符串字面量是用于简化包含有特殊含义的定界符和转义序列起始字符,例如引号和反斜杠等嵌套字符的字符串字面量。它们可用于编码HTML文本等场景。例如,与之相比:

"<a href=\"file\">C:\\Program Files\\</a>"

这是一个常规的字符串文字,附带

R"(<a href="file">C:\Program Files\</a>)"

这是一个原始字符串字面量。在这里,除了引号之外使用括号可以使C ++区分嵌套的引号和限定字符串本身的引号。


2
我不知道为什么你需要括号。这是我第一次看到它。现在显然了。请注意,您仍然可以像 uRu8R 一样添加前缀,并且这也适用于 C,但仅适用于 gnu -std=gnu99 及以后的版本。 - Lewis Kelsey
@LewisKelsey 括号是规范的一部分。C++11标准(ISO/IEC 14882:2011): 2.14.5 字符串字面量 [lex.string] - undefined

58

基本上,原始字符串字面值是指C++中的转义字符(如\n \t\")未被处理的字符串。引入于C++11的原始字符串字面值以R"(开头并以)"结尾。

前缀(可选)R "定界符(原始字符)定界符"

前缀- L、u8、u、U之一

感谢@Remy Lebeaudelimiter是可选的,通常省略,但在某些特殊情况下实际上是需要的,特别是如果字符串内容包含)"这个字符序列,例如:R"(...)"...)",那么您需要一个定界符来避免错误,例如:R"x(...)"...)x"

以下是一个示例:

#include <iostream>
#include <string> 

int main()
{
    std::string normal_str = "First line.\nSecond line.\nEnd of message.\n";
    std::string raw_str = R"(First line.\nSecond line.\nEnd of message.\n)";
    std::string raw_str_delim = R"x("(First line.\nSecond line...)")x";
    std::cout << normal_str << std::endl;
    std::cout << raw_str << std::endl;
    std::cout << raw_str_delim << std::endl;
    return 0;
}

输出:

第一行。

第二行。

消息结束。

第一行。\n第二行。\n消息结束。\n

"(第一行。\n第二行...)"

Godbolt上实时运行


3
但是在代码中,R被定义为"x",在#define扩展后,代码变成了const char* s = "x""y";,并且没有任何R"( - Chupo_cro
请问能否在“示例”中添加使用分隔符的示例?并附上输出结果。 - pmor
1
@pmor 更新了示例。 - Oblivion

3

我将对评论中的一个问题进行補充:

但是这里代码中的 R 被定义为 "x",在 #define 展开之后,代码变成了 const char* s = "x""y";并且没有任何 R"(。

问题中的代码片段是为了展示 Raw Strings 的无效用法。让我在此提供实际的三行代码:

#define R "x"
const char* s = R"y"; // ill-formed raw string literal, not "x" "y"
const char* s2 = R"(a)" "b)"; // a raw string literal followed by a normal string literal
  • 第一行是为了不被宏所混淆。宏是预处理代码片段,用于替换源代码中的部分内容。而原始字符串(Raw String)则是该语言的一个特性,根据语言规则进行“解析”。
  • 第二行是为了展示错误的用法。正确的方式应该是R"(x)",其中需要在括号内包含字符串。
  • 最后一行是为了展示如果不仔细编写可能会有问题。括号内的字符串不能包含原始字符串的结束序列。修正方法可以为R"_(a)" "b)_ "。下划线可以替换为任何字符(但不能是括号、反斜杠和空格),并且可以是任意数量的字符,只要不包含结束序列:R"___(a)" "b)___"R"anything(a)" "b)anything"

因此,如果我们将这些更正包装在一个简单的C++代码中:

#include <iostream>
using namespace std;

#define R "x" // This is just a macro, not Raw String nor definition of it
const char* s = R"(y)"; // R is part of language, not a macro
const char* s2 = R"_(a)" "b)_"; // Raw String shall not include closing sequence of characters; )_"

int main(){ cout << s <<endl << s2 <<endl << R <<endl; }

那么输出结果将会是:
y
a)" "b
x

1
多么笨拙的特性! - chqrlie

1

原始字符串字面值。用于避免任何字符的转义。定界符之间的所有内容都成为字符串的一部分。如果有前缀,则具有与上述描述相同的含义。

C++参考:字符串字面值

原始字符串定义如下:

string raw_str=R"(First line.\nSecond line.\nEnd of message.\n)";

而区别在于原始字符串忽略(转义)所有特殊字符,如 \n 和 \t,并将它们视为普通文本。因此,上述行将仅成为一行,其中包含3个实际的 \n,而不是3个单独的行。
您需要删除定义行并在字符串周围添加括号,以被视为原始字符串。

1
你确定需要移除这个定义吗?我认为如果是这种情况,那么当前状态下的示例将不是一个未定义的原始字符串,而是一个定义良好的字符串字面量。 - Christian Gibbons

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