C++11 的原始字符串文字 R"(...)" 中括号的基本原理是啥?

Posted

技术标签:

【中文标题】C++11 的原始字符串文字 R"(...)" 中括号的基本原理是啥?【英文标题】:What is the rationale for parenthesis in C++11's raw string literals R"(...)"?C++11 的原始字符串文字 R"(...)" 中括号的基本原理是什么? 【发布时间】:2013-10-05 05:46:43 【问题描述】:

在 C++11 中引入了一个非常方便的功能,称为原始字符串文字,它是没有转义字符的字符串。而不是这样写:

  regex mask("\\t[0-9]+\\.[0-9]+\\t\\\\SUB");

你可以这么写:

  regex mask(R"(\t[0-9]+\.[0-9]+\t\\SUB)");

更具可读性。但是,请注意必须放置字符串周围的额外括号以定义原始字符串文字。

我的问题是,为什么我们甚至需要这些?对我来说,它看起来很丑陋和不合逻辑。以下是我看到的缺点:

额外的冗长,而整个功能用于使文字更紧凑 很难区分文字主体和定义符号

这就是我所说的难以区分的意思:

"good old usual string literal"
 ^-    body inside quotes   -^

R"(new strange raw string literal)"
   ^- body inside parenthesis  -^

这是专业人士:

更灵活,原始字符串中可用的字符更多,尤其是与分隔符一起使用时:"delim( can use "()" here )delim"

但是,嘿,如果你需要更多的灵活性,你有旧的很好的可转义字符串文字。为什么标准委员会决定用这些绝对不必要的括号来污染每个原始字符串文字的内容?这背后的理由是什么?我没有提到的优点是什么?

UPD Kerrek 的回答很棒,但不幸的是,这不是一个答案。由于我已经描述了我了解它的工作原理以及它带来的好处。我问这个问题五年过去了,仍然没有答案。我仍然对这个决定感到沮丧。有人可以说这是一个品味问题,但我不同意。您使用多少个空格,如何命名变量,这是SomeFunction() 还是some_function() - 这是品味问题。而且我真的可以很容易地从一种风格切换到另一种风格。

但是这个?.. 这么多年了,还是觉得别扭和笨拙。不,这与味道无关。这是关于无论如何我们要如何涵盖所有可能的情况。每次我们需要编写特定于 Windows 的路径、正则表达式或多行字符串文字时,我们都注定要编写这些丑陋的括号。又是为了什么?.. 对于那些我们实际上需要将" 放入字符串中的罕见情况?我希望我能参加那个他们决定这样做的委员会会议。我强烈反对这个非常糟糕的决定。我希望。现在我们注定要失败。

感谢您阅读本文。现在我感觉好多了。

UPD2这是我的替代建议,我认为这两个建议都比现有的要好得多。

提案1.灵感来自python。不支持带三引号的字符串文字:R"""Here is a string literal with any content, except for triple quotes, which you don't actually use that often."""

提案 2。灵感来自常识。支持所有可能的字符串文字,就像当前的:R"delim"content of string"delim"。带空分隔符:R""Looks better, doesn't it?""。空的原始字符串:R""""。带双引号的原始字符串:R"#"Here are double quotes: "", thanks"#"

这些建议有什么问题吗?

【问题讨论】:

R";-](R"(this is a basic raw string literal as text inside a more complex one)");-]" 语法确实很丑 imo,但我真的想不出一个既能保持向后兼容又能保持所有功能的替代方案。 @ChilliDoughnuts,请参阅更新后的问题。 @Mikhail: "对于那些我们实际上需要将 " 放入字符串中的罕见情况?" 事实上,您认为需要 " 的情况原始字符串“稀有”可能是问题的一部分。并不是说“没有答案”。有一个答案;你只是不同意它。如果你对什么构成的定义“答案”是“说服我改变主意的东西”,那么你的问题太自以为是了。已经提供了理由;不需要你同意。 您不应更新历史上被高度评价的问题以包含一个新问题……而是发布一个新问题。 (无论如何,这可能会因基于意见而被关闭,因为您唯一的反对意见似乎是“我觉得这不美观”) 【参考方案1】:

括号的目的是允许您指定自定义分隔符:

R"foo(Hello World)foo"   // the string "Hello World"

在您的示例和典型使用中,分隔符只是空的,因此原始字符串由序列R"()" 括起来。

允许任意分隔符是一种设计决策,它反映了提供一个没有奇怪限制或极端情况的完整解决方案的愿望。您可以选择 任何 字符串中未出现的字符序列作为分隔符。

如果没有这个,如果字符串本身包含类似"(如果您只想将R"..." 作为原始字符串语法)或)"(如果分隔符为空),那么您将遇到麻烦。这两个都是非常常见和频繁的字符序列,尤其是在正则表达式中,所以如果你是否使用原始字符串的决定取决于你的字符串的具体内容,那将是非常烦人的。

请记住,在原始字符串内部没有其他转义机制,因此您可以做的最好的事情是连接字符串文字片段,这是非常不切实际的。通过允许自定义分隔符,您只需选择一次不寻常的字符序列,也许在极少数情况下在您以后进行编辑时对其进行修改。

但再次强调,即使是空分隔符也已经很有用了,因为R"(...)" 语法允许您在字符串中放置裸引号。这本身就是一个很大的收获。

【讨论】:

还有裸换行符、制表符和空格! 当然,只是强调 () 不存在允许反斜杠和空格。仅当您有一个带有 )" 的字符串时才需要分隔符。例如 R"("(eg)")" 必须使用分隔符 R"delim("(eg)"))delim"。我喜欢同意语法有点笨拙,在这个例子中,“\”(eg)\“”对我来说更具可读性。 @AndyG:我的意思是)foo 没有出现在你的字符串中,包括括号。 d-char-sequence 本身确实可以任意出现。 @Mikhail:您不需要对每个字符串都使用原始字符串文字。这是一个判断电话;当它改善问题时使用它。典型的用例将有一个长或复杂的字符串,这样您就可以专注于正文并在阅读时基本上忽略分隔符。 @KerrekSB 更准确地说,)foo 也可以出现在字符串中,但 )foo" 不能。 R"foo(Hello World )foo)foo" 等价于 "Hello World )foo"【参考方案2】:

正如另一个答案所解释的,引号中必须有一些额外的内容,以避免在 ")" 或实际上可能出现在字符串本身中的任何关闭序列的情况下出现解析歧义。

至于语法选择,嗯,我同意语法选择是次优,但总的来说还可以(你可以这么想:“事情可能会更糟”,哈哈)。我认为这是使用简单性和解析简单性之间的一个很好的折衷。

提案 1。灵感来自蟒蛇。不能支持字符串文字 三引号: R"""任何内容,除了三引号,你 实际上并不经常使用。"""

这确实有一个问题-“引号,您实际上并不经常使用”。 首先,原始字符串的真正想法是表示 raw 字符串,即与它们在文本文件中出现的完全一样,无需对字符串进行 任何 修改,无论字符串如何内容。其次,语法应该是通用的,即不添加“几乎原始字符串”等变体。

你会如何用这种语法写一个引号?两个引号?注意 - 这些是非常常见的情况,尤其是当您的代码处理字符串和解析时。

提案 2。 R“delim”字符串“delim”的内容。 R""看起来更好,不是吗?""。 R"#"这里是双引号:"",谢谢"#"。

嗯,这个可能是更好的候选人。但有一件事 - 一个常见的情况(我相信这是接受语法的一个激励案例),双引号字符本身是 very 常见的,原始字符串应该在这些情况下派上用场。

所以,让我们看看,普通的字符串语法:

s1 = "\"";
s2 = "\"quoted string\"";

您的语法,例如用“x”作为分隔符:

s1 = R"x"""x";
s2 = R"x""quoted string""x";

接受的语法:

s1 = R"(")";
s2 = R"("quoted string")";

是的,我同意括号引入了一些令人讨厌的视觉效果。所以我怀疑语法的作者认为在这种情况下很少需要额外的“delim”,因为)" 并不经常出现在字符串中。但是OTOH,尾随/前导/孤立的引号很常见,例如您建议的语法 (#2) 需要更频繁地使用delim,这反过来又需要更频繁地将其从R"".."" 更改为R"delim"..."delim"。希望你能明白。

语法可以更好吗?我个人更喜欢更简单的语法变体:

Rdelim"string contents"delim;

结合上面的例子:

s1 = Rx"""x; 
s2 = Rx""quoted string""x;

但是要正常工作(如果在当前语法中可能的话),此变体将需要限制 delim 部分的字符集,仅说是字母/数字(因为现有的运算符),也许还有一些初始字符的限制,以避免与未来可能的语法发生冲突。 所以我相信可以做出更好的选择,尽管在这种情况下没有什么显着更好。

【讨论】:

感谢您的详细回答!这实际上更接近我想看到的。 “OTOH,经常出现尾随/前导/孤立的引号”-好吧,我没有这种感觉。但这只是我的感觉。也许如果你分析一个巨大的公共代码库,你会发现事实就是如此。但同样,对我来说感觉不同。 带有“引用字符串”的好例子。但是,嘿,您是否想说在 all 情况下,原始字符串文字 应该 看起来尽可能好?我只想在非原始字符串文字不够好的情况下优化它们。对于您的两个示例,我实际上更喜欢使用非原始字符串文字。这就是为什么我不太关心它会如何查找原始字符串文字。但我明白你的意思。谢谢。 @Mikhail “用于非原始字符串文字不够好的情况”。我可能需要某种转义的任何文字都不适合许多任务(例如,放置带有 DSL 内容的字符串,例如 JSON、Regex 等)所以我只是说这种文字 IMO 必须是真正的原始字符串,而不是一些半生不熟的东西,因此现有的语法符合我对正确技术解决方案的期望。 是的,必须注意分隔符,但这至少比转义序列更明显。如果一个字符串在解析阶段终止于错误的位置 - 很可能您会看到一些错误,但如果序列转义不正确,则难以发现错误的情况更多,而且打字更烦人。

以上是关于C++11 的原始字符串文字 R"(...)" 中括号的基本原理是啥?的主要内容,如果未能解决你的问题,请参考以下文章

“u”和“r”字符串标志到底是做啥的,啥是原始字符串文字?

在原始字符串文字中包含 )" 而不终止所述文字

C++11 原始字面量

在 Python 2 和 Python 3 中有效的原始 unicode 文字?

[C++11]字符串原始字面量

托管 C++ 中的逐字字面量? (就像 C# 的 @"blah")