今天说一个C++11 引入一个小但非常有用的特性--原始字符串(raw string literal)。

背景

在每个程序中,几乎都应用到字符串字面量。但传统的字符串字面量的语法对带有特殊字符的字面量的支持不友好,它引入了一些转义字符来表示字符串中这些特殊的字符,如:

\a 响铃(BEL)
\b 退格(BS)
\f 换页(FF)
\n 换行(LF)
\r 回车(CR)
\t 水平制表(HT)
\v 垂直制表(VT)
\\ 反斜杠
\? 问号字符
\' 单引号字符
\" 双引号字符
\0 空字符(NULL)

这所带来的问题对于简单的字符串并不算大,但对一些特殊的应用明显不给力,比如,正则表达式:
\\d{3}-\\d{8}|\\d{4}-\\d{7}
因为正则表达式大量使用了\,而反斜杠被用来作为转义序列的开始,所以在字符串中需要使用\\进行转义,这种语法虽然能解决问题,明显,可读性是非常差的。若应用场景再复杂点,估计很多人为了数清楚反斜杠得耗掉不少的时间。

如果能直接表示成\d{3}-\d{8}|\d{4}-\d{7},该有多好!

语法

C++11 终于行动了,引入了原始字符串。最基本的用法是R"(...)",由R开头,双引号内包围着(...),实际的字符序列是小括号内的内容,小括号是字符序列的定界符。当然,左小括号和右小括号是首位对应的。

“原始”(raw)体现在字符串里的字符一就是一,二就是二,不会给你转义。也就是说,传统的"\n"除了字符串结尾符,仅包含换行符,而原始字符串R"(\n)"则包含反斜杠和字符n,这是明显的不同。

从现在来看,貌似已经很好的解决问题了,但如果字符序列里包含)",如R"(坐标: "(x,y)")",此时编译器是懵的,因为"(对应了两个)"。在这种情况下,我们可以选择其他定界符,如,R"&(坐标: "(x,y)")&"。语法如下:
R"delim(...)delim"delim的选取比较灵活,最长不超过16个字符,且不为小括号、空白、控制字符和反斜杠。

应用

背景中给了原始字符串在正则表达式的应用,但其应用场景不止于此,比如:

  • Windows文件路径

R"(C:\ProgramFiles\xx\xx\xx.exe)"

  • Json字符串

R"({"name":"xx","age":10})"

例子

在洛谷的第一道题:
图片描述

这道题可以使用其他技巧解答,这里仅给出使用原始字符串的解法:

#include<iostream>

int main() {
    std::cout << R"(                ********
               ************
               ####....#.
             #..###.....##....
             ###.......######              ###            ###
                ...........               #...#          #...#
               ##*#######                 #.#.#          #.#.#
            ####*******######             #.#.#          #.#.#
           ...#***.****.*###....          #...#          #...#
           ....**********##.....           ###            ###
           ....****    *****....
             ####        ####
           ######        ######
##############################################################
#...#......#.##...#......#.##...#......#.##------------------#
###########################################------------------#
#..#....#....##..#....#....##..#....#....#####################
##########################################    #----------#
#.....#......##.....#......##.....#......#    #----------#
##########################################    #----------#
#.#..#....#..##.#..#....#..##.#..#....#..#    #----------#
##########################################    ############ )";
}

通过上面的例子,我们也能发现原始字符串的另外两个优点:

  1. 换行能很好的保留
    其他方式都需要人工加\n实现换行。
  2. 方便拷贝字符串
    将拷贝的字符串直接替换R"(...)"中的...,任务就完成了,非常方便。而其他方式,都需要过多的干预拷贝的字符串,非常容易出错。

请继续关注我的公众号文章
图片描述


ideami
91 声望11 粉丝