The New C++ -- 基本数据类型和字面值常量 (8. 字符串字面值常量)


顾名思义,字符串就是将字符串起来。如果你已经理解了字符型和字符字面值常量,那么字符串字面值常量就不难理解了。字符字面值常量是由单引号将一个字符引起来,而字符串字面值常量是由双引号将一些字符引起来。字符串里的特殊字符的例如转义字符(除了单引号和双引号转义字符),八进制转义字符,十六进制转义字符,\u和\U所表达的意义和字符字面值常量都是一样的。

C++在所有的字符串后面多加上一个字符 '\0',表示字符串的结束。例如"abcd"这个字符串是由5个char类型字符组成:'a','b','c','d'和 '\0'。字符串前面可以加前缀u,U或者L分别表示这个字符串由char,char16_t,char32_t和wchar_t类型的字符组成。例如u"abcd"由5个char16_t的字符组成:u'a',u'b',u'c',u'd'和u'\0'。

虽然我们前面说过,当Unicode转义字符\U用在char16_t类型的字符字面值常量中时,不能超过0xFFFF的范围,但是用在char16_t类型的字符串字面值常量中时,可以超过0xFFFF(但不能超过0x10FFFF),这时他表示两个char16_t的字符,编译器会自动转换。例如u"abc\U00015678"表示6个char16_t的字符,他等值于u"abc\uD815\uDE78"。其中0xD815 0xDE78是Unicode码点0x15678的UTF-16编码。同理,当十六进制转义字符和\u以及\U用在L位前缀的字符串字面值常量的时候,编译器会自动将他们转化位执行字符集的编码(如Visual C++转为UTF-16,GCC转为UTF-32)。

C++中还定义了UTF-8编码的字符串字面值常量,前缀是u8,例如u8"abcd"。里面的每个字符类型为char。你也许注意到了普通的没有前缀的字符串字面值常量和u8为前缀的字符串字面值常量都是由char类型的字符组成的,但u8有特殊意图,表示字符串采用Unicode中的UTF-8编码。而没有前缀的字符串在几乎所有的现代编译器中都采用ASCII编码。

当\u或者\U用于u8为前缀的字面值常量中时,分别代表2-4个字符,编译器会自动转换。例如,u8"abc\U00015678"表示8个char的字符,他等值于u"abc\xF0\x95\x99\xB8",其中0xF0 0x95 0x99 0xB8是Unicode码点0x15678的UTF-8编码。你也许有疑问为什么没有表达UTF-8的字符类型,那是因为表达UTF-8的字符类型只能表达0x00到0x7F之间的码点(超过就要至少2个字符才能表达),并且这和ASCII在同一个范围(0x00-0x7F)表达的字符是一样的,所以char就足够了,再多一个类型是没有必要的。

当相邻的两个字符串字面值常量之间只有零个或多个空格,制表符,或者换行之类的字符的时候,这些字符串常量将会被连接成一个字符串字面值常量,例如,"abc" "def" "123"
将被连接成为一个字符串字面值常量"abcdef123"。

当两个有前缀的字符串字面值常量被连接时,如果都没有前缀,则结果也没有前缀;如果前缀相同,则结果也是那个前缀;如果一个有前缀,另一个没有前缀,结果是有前缀的那个前缀。其他的情况都是未定义的(注脚:未定义行为(undefined-behavior)是C++中最令人头疼的最邪恶的现象。为了让不同的编译器进行优化,为了让编译器的实现简单一些,C++规定了各种未定义行为。当你的程序中涉及到了未定义行为,该程序的运行结果在C++的标准中是未定义的,一个未定义运行结果的程序也是无用的。)。例如,u"a" "b"和"a" u"b"都会被连接成u"ab"。

练习:


在屏幕输出『我是C++程序员』。




当字符串字面值常量里面有很多转义字符的时候,就会很容易出错。例如一个正则表达式可以写成,


"('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|"



你甚至可以不用知道正则表达式是什么,你也绝不会怀疑,这样写是很容易犯错,也很难除错的。C++支持原生字符串(raw string)的表达。原生字符串里面可以直接用反斜线\,单引号',双引号"之类的特殊字符,而不需要用(也无法用)他们的转义字符。原生表达式的语法是,

R"delimiter-string(raw-string)delimiter-string"

其中raw-string表示真正想表达的字符串字面值常量,R是前缀表示原生字符串,而delimiter-string表示任何长度小于16的字母或者数字组成的字符串(长度可能是0)(注脚:不包括 括号,(),反斜线\,制表符等等的特殊字符。)。前一个delimiter-string和后一个delimiter-string必须相同,这样delimiter-string(表示原生字符串的开始,)delimiter-string表示原生字符串的结束。

用例子最好解释,R"(raw-string)",R"[(raw-string)]",R"abc(raw-string)abc",R"@@(raw-string)@@"都表示原生字符串字面值常量。delimiter-string的选择要保证raw-string里面不含有")delimiter-string"这样的字眼,否则程序会报错。上面那个正则表达是的例子用原生字符串可以写成,


R"zzz(('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|\)zzz"


原生字符串里可以直接写出单引号,双引号,反斜线,换行符,制表符,也可以使用左括号(和右括号),不需要任何转义字符。事实上,也写不出转义字符。例如下面这行程序,

   
std::cout << R"(abc\tdef\n)" << std::endl;


他的输出是,
abc\tdef\n


原生字符串字面值常量同样适用于前缀为u8,u,U和L的字符串,例如LR"(abc)"。但要注意的一点是,八进制转义字符,十六进制转义字符,Unicode转义字符\u和\U,都无法在原生字符串中表达原有的意思。

小知识:Visual C++中"你好"也可以当成普通的char类型的字符串字面值常量用,而不是宽字符串字面值常量。"你好"在这里会被转换为4个字节的GB2312编码(和编辑器的设置有关)。但如果是宽字符串字面值常量例如L"你好"或者u"你好",Visual C++会将这两个字转换为Unicode编码。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值