C++原始字符串字面量

Share on:
400 Words | Read in about 2 Min | View times

Overview

在传统C++中,字符串字面量的语法对一些特殊字符并不友好,需要通过转义字符来表示这些特殊字符。这对简单字符串计算量并不大,但在一些特殊情况如正则表达式中,可读性和性能就会非常差。因此C++11引入了原始字符串字面量(raw string literal)来解决字符串的表达形式。

本系列文章将包括以下领域:

本章其他内容请见 《现代C++》

在传统C++中,字符串字面量的语法对一些特殊字符并不友好,需要通过转义字符来表示这些特殊字符。

常见的转义字符有:

转义字符 含义 ASCII
\a 响铃 7
\b 退格 8
\f 换页 12
\n 换行 10
\r 回车 13
\t 水平制表 9
\v 垂直制表 11
' 单引号 39
" 双引号 34
\ 单反斜杠 92
? 问号 63
\0 空字符 0
\ooo 用1-3位八进制数000为码值所对应的字符 ooo
\xhh 用1-2位十六进制数hh为码值所对应的字符 hh

这对简单字符串计算量并不大,但在一些特殊情况如正则表达式中,比如\\d{3}-\\d{8}|\\d{4}-\\d{7},可读性和性能就会非常差。由于\被用作转义字符的开始,所以字符串中需要出现\时就需要用\\进行转义,对于非常长的转义字符串来说,可读性真的很差,而且容易出错。

因此C++11引入了原始字符串字面量(raw string literal)来解决字符串的表达形式。上面的字符串就可以简化为\d{3}-\d{8}|\d{4}-\d{7}

基本用法

原始字符串字面量的基本语法为R"(...)",以R为开头,双引号包围()...中的内容不会进行任何转移,其中()叫做原始字符串的定界符。

如果原始字符串中需要包含"()",此时需要选择其他界定符。界定符默认是(),可以重新指定。界定符首尾相对,最长不超过16个字符,且不包括小括号、空白、控制字符和反斜杠。

举例:

1auto str1 = R"&coordinate:"(x, y)"&";
2auto str2 = R"%%He says "Nice to meet you!"%%";

应用场景

  • 正则表达式

R"(\d{3}-\d{8}|\d{4}-\d{7})"

  • Windows文件路径

R"(C:\Users\zhxilin\Documents\README.md)"

  • Json

R"({"name": "zhxilin", "language": "c++"})"

  • 与Unicode字符串结合

C++11支持Unicode,原始字符串的定义可以与Unicode字符串结合。UTF-8、UTF-16、UTF-32都是Unicode的具体实现编码。定义UTF-8、UTF-16、UTF-32的原始字符串,需要用前缀u8RuRUR

 1auto& s0 = "\u4f60\u597d";
 2std::cout << "s0 = " << s0 << ", sizeof(s0) = " << sizeof(s0) << std::endl;
 3auto& s1 = u8R"(你好)";
 4std::cout << "s1 = " << s1 << ", sizeof(s1) = " << sizeof(s1) << std::endl;
 5auto& s2 = uR"(你好)";
 6std::cout << "s2 = " << s2 << ", sizeof(s2) = " << sizeof(s2) << std::endl;
 7auto& s3 = UR"(你好)";
 8std::cout << "s3 = " << s3 << ", sizeof(s3) = " << sizeof(s3) << std::endl;
 9
10std::cout << "--------"  << std::endl;
11
12auto& s4 = u8R"(zhxilin)";
13std::cout << "s4 = " << s4 << ", sizeof(s4) = " << sizeof(s4) << std::endl;
14auto& s5 = uR"(zhxilin)";
15std::cout << "s5 = " << s5 << ", sizeof(s5) = " << sizeof(s5) << std::endl;
16auto& s6 = UR"(zhxilin)";
17std::cout << "s6 = " << s6 << ", sizeof(s6) = " << sizeof(s6) << std::endl;

运行结果:

1s0 = 你好, sizeof(s0) = 7
2s1 = 你好, sizeof(s1) = 7
3s2 = 0x557972cd2026, sizeof(s2) = 6
4s3 = 0x557972cd2044, sizeof(s3) = 12
5--------
6s4 = zhxilin, sizeof(s4) = 8
7s5 = 0x557972cd208e, sizeof(s5) = 16
8s6 = 0x557972cd20b8, sizeof(s6) = 32

从运行结果可以看出,使用\u定义的Unicode字符和使用u8R定义的原始字符串字面量的输出结果一样。使用不同编码方式进行编码的原始字符串字面量占用的空间大小不一样。使用UTF-16和UTF-32编码进行输出时只打印了字符串的地址。

  • 利用原始字符串字面量进行字符串拼接

相同编码的原始字符串字面量可以进行连接,中间需要一个空格。不同编码不可连接,编译器会报错:

1auto& s7 = R"(你好)" R"(zhxilin)";
2std::cout << "s7 = " << s7 << ", sizeof(s7) = " << sizeof(s7) << std::endl;
3
4//auto& s8 = u8R"(你好)" uR"(zhxiLin)"; //error: unsupported non-standard concatenation of string literals
5//std::cout << "s8 = " << s8 << ", sizeof(s8) = " << sizeof(s8) << std::endl;

运行结果:

1s7 = 你好zhxilin, sizeof(s7) = 14

结合前一个例子可以看到,“你好"的大小是7,两个UTF-8字符大小是6,加一个空字符;“zhxilin"的大小是8,UTF-8字符大小是7,加一个空字符。连接之后只需要末尾一个空字符,所以大小一共是6 + 7 + 1 = 14

Prev Post: 『C++类内初始化』
Next Post: 『C++用户定义字面量』