C ++ 11的原始字符串文字R“(…)”中括号的基本原理是什么?


75

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"#"

这些建议有什么问题吗?


13
R";-](R"(this is a basic raw string literal as text inside a more complex one)");-]"
pepper_chico

imo的语法确实很丑陋,但是我真的想不出一个可以保持向后兼容并保留所有功能的替代方法。
ChilliDoughnuts

@ChilliDoughnuts,请参阅更新的问题。
米哈伊尔

1
@Mikhail:“对于那些真正需要在字符串中添加的罕见情况?“您认为"原始字符串中需要的案例是“稀有”的事实可能是问题的一部分。这并不是说没有答案。有答案;您只是不同意。如果您对什么是“答案”的定义是“东西说服我改变我的脑海里对这个”,那么你的问题太自以为是的理由已经提供;不需要用它的协议。
尼科尔流星锤

1
您不应该将历史性的问题高度评价为包含新问题...而是发布新问题。(无论如何,由于您的唯一反对意见似乎是“我觉得这很不美观”,因此无论如何都将基于观点关闭)
MM

Answers:


8

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

至于语法选择,我同意语法选择不是 次优的,但总体来说还可以(您可能会想到:“情况可能会更糟”,大声笑)。我认为这是用法简单和解析简单之间的良好折衷。

提案1。受python启发。无法支持带有三引号的字符串文字:
R“”“任何内容,除了三引号之外,您实际上并不经常使用它。”“

确实存在一个问题-“引用,您实际上并不经常使用它”。首先,原始字符串的基本思想是表示原始字符串,即与它们在文本文件中显示的样子完全一样,而无需对字符串进行任何修改,而与字符串内容无关。其次,语法应该是通用的,即不要添加“几乎原始字符串”等变体。

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

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

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

因此,让我们看一下普通的字符串语法:

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

您的语法,例如以“ x”为delim的语法:

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,经常在行尾/前导/隔离引号”-好吧,我没有这种感觉。但这只是我的感觉。也许,如果您分析大量的公共代码库,您会发现实际上是这样。但是再次,对我来说,感觉有所不同。
米哈伊尔(Mikhail),

带有“带引号的字符串”的好例子。但是,您是否要说原始字符串文字在所有情况下应该看起来尽可能好?我只想对非原始字符串文字不够好的情况进行优化。对于您的两个示例,我实际上都希望使用非原始字符串文字。这就是为什么我不在乎它如何寻找原始字符串文字。但我明白你的意思。谢谢。
米哈伊尔(Mikhail)

@Mikhail“用于非原始字符串文字不够好的情况”。我可能需要某种转义的任何文字都不能胜任许多任务(例如,将带有DSL内容的字符串放入字符串,例如JSON,Regex等),所以我只是说,这种文字IMO必须是真实的原始字符串,而不是半生半熟,因此现有语法符合我对正确技术解决方案的期望。
Mikhail V

是的,必须注意分隔符,但这至少比转义序列可见。如果字符串在解析阶段在错误的地方终止-很可能您会看到一些错误,但是如果错误地对序列进行了转义,则更多情况下会出现难以发现的错误,并且输入时会更加烦恼。
米哈伊尔五世

101

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

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

在您的示例(通常使用)中,定界符只是空的,因此原始字符串被序列R"(和包围)"

允许使用任意定界符是一项设计决策,反映了提供无怪异限制或边缘情况的完整解决方案的愿望。您可以选择字符串中没有出现的任何字符序列作为定界符。

否则,如果字符串本身包含类似"(如果您只是想R"..."作为原始字符串语法)或)"(如果分隔符为空)之类的内容,将会给您带来麻烦。这两个都是非常常见且频繁的字符序列,尤其是在正则表达式中,因此,如果决定是否使用原始字符串取决于字符串的特定内容,将是非常令人讨厌的事情。

请记住,在原始字符串中没有其他转义机制,因此,您可能要做的最好的办法是将字符串文字串接起来,这是非常不切实际的。通过允许使用自定义定界符,您需要做的就是一次选择一个不寻常的字符序列,并可能在以后进行编辑的极少数情况下对其进行修改。

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


9
还有裸换行符,制表符和空格!
ПетърПетров

3
当然,只需突出显示()不允许反斜杠和空格。仅当您在其中带有)“的字符串时才需要定界符。例如R”(“(”(eg)“))”必须使用定界符R“ delim(”(eg“”))delim“。的同意语法有点笨重,在这个例子中,“\”(例如)\“”,是更易读我。
超级苍蝇乔恩

1
@AndyG:我的意思是它)foo不会出现在您的字符串中,包括括号在内。在d-字符序列本身确实可能出现随意。
Kerrek SB

3
@Mikhail:您不需要为每个字符串都使用原始字符串文字。这是一个判断电话;当它改善事情时使用它。典型的用例可能是长字符串,也可能是复杂字符串,因此您将注意力集中在正文上,并且在阅读时基本上会忽略分隔符。
Kerrek SB

7
更准确地说,@ KerrekSB)foo也可以出现在字符串内部,但)foo"不能出现。R"foo(Hello World )foo)foo"等价于"Hello World )foo"
isarandi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.