本文内容
C++支持各种字符串和字符类型,并提供了表示每种类型的文字值的方法。 在源代码中,字符集用于表示字符和字符串文字的内容。 通用字符名称和转义字符允许您仅使用基本源字符集来表示任何字符串。 原始字符串允许您避免转义字符,并且可用于表示所有类型的字符串。 也可以创建 std:: 而无需执行额外的构造或转换步骤。
#include
using namespace std::string_literals; // enables s-suffix for std::string literals
int main()
{
// Character literals
auto c0 = 'A'; // char
auto c1 = u8'A'; // char
auto c2 = L'A'; // wchar_t
auto c3 = u'A'; // char16_t
auto c4 = U'A'; // char32_t
// Multicharacter literals
auto m0 = 'abcd'; // int, value 0x61626364
// String literals
auto s0 = "hello"; // const char*
auto s1 = u8"hello"; // const char* before C++20, encoded as UTF-8,
// const char8_t* in C++20
auto s2 = L"hello"; // const wchar_t*
auto s3 = u"hello"; // const char16_t*, encoded as UTF-16
auto s4 = U"hello"; // const char32_t*, encoded as UTF-32
// Raw string literals containing unescaped \ and "
auto R0 = R"("Hello \ world")"; // const char*
auto R1 = u8R"("Hello \ world")"; // const char* before C++20, encoded as UTF-8,
// const char8_t* in C++20
auto R2 = LR"("Hello \ world")"; // const wchar_t*
auto R3 = uR"("Hello \ world")"; // const char16_t*, encoded as UTF-16
auto R4 = UR"("Hello \ world")"; // const char32_t*, encoded as UTF-32
// Combining string literals with standard s-suffix
auto S0 = "hello"s; // std::string
auto S1 = u8"hello"s; // std::string before C++20, std::u8string in C++20
auto S2 = L"hello"s; // std::wstring
auto S3 = u"hello"s; // std::u16string
auto S4 = U"hello"s; // std::u32string
// Combining raw string literals with standard s-suffix
auto S5 = R"("Hello \ world")"s; // std::string from a raw const char*
auto S6 = u8R"("Hello \ world")"s; // std::string from a raw const char* before C++20, encoded as UTF-8,
// std::u8string in C++20
auto S7 = LR"("Hello \ world")"s; // std::wstring from a raw const wchar_t*
auto S8 = uR"("Hello \ world")"s; // std::u16string from a raw const char16_t*, encoded as UTF-16
auto S9 = UR"("Hello \ world")"s; // std::u32string from a raw const char32_t*, encoded as UTF-32
}
字符串文字可以以 m 或 u8、L、u 和 U 为前缀,分别表示窄字符(单字节或多字节)、UTF-8、宽字符(UCS-2 或 UTF-16)。 UTF-16 和 UTF-32 编码。 原始字符串文字可以具有 R、u8R、LR、uR 和 UR 前缀,以表示这些编码的原始版本等效项。 要创建临时或静态 std:: 值,可以使用字符串文字或带有 s 后缀的原始字符串文字。 有关详细信息,请参阅下面的部分。 有关基本源字符集、通用字符名称以及源代码中扩展代码页字符的使用的详细信息,请参阅字符集。
字符字面量
字符文字由字符常量组成。 它由用单引号括起来的字符表示。 字符文字有五种类型:
用于字符文字的字符可以是除保留字符反斜杠 (\)、单引号 (') 和换行符之外的任何字符。 可以使用转义序列指定保留字符。 可以使用通用字符名称来指定字符,只要类型的大小足以容纳字符即可。
编码
字符文字根据其前缀进行不同的编码。
转义序列
转义序列分为三种类型:简单、八进制和十六进制。 转义序列可以具有以下任意值:
值转义序列
越线
\n
反斜杠
\\
水平制表符
\t
问号
垂直制表符
\v
撇号
\'
退格键
\b
双引号
\"
回车
\r
空字符
\0
换页
\F
八进制
\呜呜
警钟)
\A
十六进制
\xhh
八进制转义序列由反斜杠后跟 1 到 3 个八进制数字的序列组成。 如果在第三个数字之前遇到八进制转义序列,则转义序列将在第一个非八进制数字的字符处终止。 可能的最高八进制值为 \377。
十六进制转义序列由一个反斜杠、后跟 x 字符和一系列一个或多个十六进制数字组成。 前导零将被忽略。 在普通或以 u8 为前缀的字符文字中,最高的十六进制值为 0xFF。 在以 L 或 u 为前缀的宽字符文字中,表示最大十六进制值。 在使用 U 前缀的宽字符文字中,表示最大十六进制值。
此示例代码演示了使用普通字符文字的转义字符的一些示例。 相同的转义序列语法对于其他字符文字类型也有效。
#include
using namespace std;
int main() {
char newline = '\n';
char tab = '\t';
char backspace = '\b';
char backslash = '\\';
char nullChar = '\0';
cout << "newline character: " << newline << "ending" << endl;
cout << "Tab character: " << tab << "ending" << endl;
cout << "Backspace character: " << backspace << "ending" << endl;
cout << "Backslash character: " << backslash << "ending" << endl;
cout << "Null character: " << nullChar << "ending" << endl;
}
反斜杠字符 (\) 位于行尾时充当行继续字符。 如果您希望反斜杠字符显示为字符文字,则必须在一行中键入两个反斜杠 (\\)。 有关行继续字符的更多信息,请参阅 。
投入的
为了从窄多字符文字创建值,编译器会将单引号之间的字符或字符序列转换为 32 位整数内的 8 位值。 文本中的多个字符根据需要从高位到低位填充相应的字节。 然后,编译器遵循将整数转换为目标类型的正常规则。 例如,要创建 char 值,编译器会采用低位字节。 为了创建 OR 值,编译器采用低位字。 如果在分配的字节或字上设置了任何位,编译器会警告结果被截断。
char c0 = 'abcd'; // C4305, C4309, truncates to 'd'
wchar_t w0 = 'abcd'; // C4305, C4309, truncates to '\x6364'
int i0 = 'abcd'; // 0x61626364
包含超过三位数字的八进制转义序列将被视为 3 位八进制序列,其后的数字将被视为多字符文本中的字符,这可能会导致令人惊讶的结果。 例如:
char c1 = '\100'; // '@'
char c2 = '\1000'; // C4305, C4309, truncates to '0'
包含非八进制字符的转义序列计算结果为完全由八进制字符组成的八进制序列,后跟剩余字符作为多字符文字中的后续字符。 如果第一个非八进制字符是十进制数字,则会生成警告 C4125。 例如:
char c3 = '\009'; // '9'
char c4 = '\089'; // C4305, C4309, truncates to '9'
char c5 = '\qrs'; // C4129, C4305, C4309, truncates to 's'
值高于 \377 的八进制转义序列会导致错误 C2022:“value-in-”:对于字符来说太大。
对于同时具有十六进制和非十六进制字符的转义序列,其计算结果为由十六进制转义序列组成的多字符文字,该十六进制转义序列完全由十六进制字符组成,后跟非十进制十六进制字符。 不包含十六进制数字的十六进制转义序列将导致编译器错误 C2153:“十六进制文字必须至少有一个十六进制数字”。
char c6 = '\x0050'; // 'P'
char c7 = '\x0pqr'; // C4305, C4309, truncates to 'r'
如果以 L 为前缀的宽字符文字包含多字符序列,则从第一个字符获取该值,并且编译器会引发警告 C4066。 后续字符将被忽略,这与等效的普通多字符文字的行为不同。
wchar_t w1 = L'\100'; // L'@'
wchar_t w2 = L'\1000'; // C4066 L'@', 0 ignored
wchar_t w3 = L'\009'; // C4066 L'\0', 9 ignored
wchar_t w4 = L'\089'; // C4066 L'\0', 89 ignored
wchar_t w5 = L'\qrs'; // C4129, C4066 L'q' escape, rs ignored
wchar_t w6 = L'\x0050'; // L'P'
wchar_t w7 = L'\x0pqr'; // C4066 L'\0', pqr ignored
“特定于”部分就结束了。
通用角色名称
在字符文字和本机(非原始)字符串文字中,任何字符都可以由通用字符名称表示。 通用字符名称由前缀 \U 后跟八位数字或前缀 \u 后跟四位数字组成。 所有八位或四位数字必须分别出现以形成格式正确的通用字符名称。
char u1 = 'A'; // 'A'
char u2 = '\101'; // octal, 'A'
char u3 = '\x41'; // hexadecimal, 'A'
char u4 = '\u0041'; // \u UCN 'A'
char u5 = '\U00000041'; // \U UCN 'A'
代理对
通用字符名称无法对代理代码点范围 D800-DFFF 中的值进行编码。 对于代理项对,使用 \ 指定通用字符名称(其中是字符的八位字节数字)。 如果需要,编译器将生成代理对。
在 C++03 中,该语言仅允许通过其通用字符名称来表示字符的子集,并且某些通用字符名称实际上并不表示任何有效字符。 此错误已在 C++11 标准中得到解决。 在 C++11 中,字符和字符串文字以及标识符可以使用通用字符名称。 有关通用角色名称的更多信息,请参阅集合。 有关详细信息,请参阅。 有关代理对的更多信息,请参阅代理对和补充字符。
字符串字面量
字符串文字表示共同形成以 null 结尾的字符串的字符序列。 字符必须放在双引号之间。 字符串文字有以下类型:
窄字符串文字
窄字符串文字是 const char[n] 类型的无前缀、双引号分隔、以 null 结尾的数组,其中 n 是数组的长度(以字节为单位)。 窄字符串文字可以包含除双引号 (")、反斜杠 (\) 或换行符之外的所有图形字符。窄字符串文字还可以包含上面列出的转义序列和通用字符名称。
const char *narrow = "abcd";
// represents the string: yes\no
const char *escaped = "yes\\no";
UTF-8编码的字符串
UTF-8 编码字符串是一个以 u8 分隔、以 null 结尾的 const char[n] 类型数组,用双引号分隔,其中 n 是编码数组的长度(以字节为单位)。 以 u8 为前缀的字符串文字可以包含除双引号 (")、反斜杠 (\) 或换行符之外的所有图形字符。以 u8 为前缀的字符串文字还可以包含上面列出的转义序列和任何常见字符名称。
C++20 引入了可移植(UTF-8 编码的 8 位)字符类型。 在 C++20 中,u8 文字前缀指定 char 以外的字符或字符串。
// Before C++20
const char* str1 = u8"Hello World";
const char* str2 = u8"\U0001F607 is O:-)";
// C++20 and later
const char8_t* u8str1 = u8"Hello World";
const char8_t* u8str2 = u8"\U0001F607 is O:-)";
宽字符串文字
宽字符串是一个以 null 结尾的常量数组,前缀为“L”,包含除双引号 (")、反斜杠 (\) 或换行符之外的所有图形字符。宽字符串文字可以包含上述列转义序列和任何通用字符列出的角色名称。
const wchar_t* wide = L"zyxw";
const wchar_t* newline = L"hello\ngoodbye";
和 (C++11)
C++11 引入了可移植(16 位)和(32 位)字符类型:
auto s3 = u"hello"; // const char16_t*
auto s4 = U"hello"; // const char32_t*
原始字符串文字 (C++11)
原始字符串是一个以 null 结尾的数组(任何字符类型),其中包含所有图形字符,包括双引号 (")、反斜杠 (\) 或换行符。原始字符串通常在使用 HTML 字符串的字符类正则表达式和XML 字符串。有关示例,请参阅以下文章:有关 C++11 的常见问题。
// represents the string: An unescaped \ character
const char* raw_narrow = R"(An unescaped \ character)";
const wchar_t* raw_wide = LR"(An unescaped \ character)";
const char* raw_utf8a = u8R"(An unescaped \ character)"; // Before C++20
const char8_t* raw_utf8b = u8R"(An unescaped \ character)"; // C++20
const char16_t* raw_utf16 = uR"(An unescaped \ character)";
const char32_t* raw_utf32 = UR"(An unescaped \ character)";
分隔符是用户定义的序列,最多 16 个字符,紧邻原始字符串文字的左括号之前和右括号之后。 例如,R"abc(Hello"\()abc",分隔符序列为abc,字符串内容为Hello"\(。可以使用分隔符来消除同时包含双引号和括号的原始字符串。这个字符串文字结果会导致编译器错误:
// meant to represent the string: )"
const char* bad_parens = R"()")"; // error C2059
但分隔符可以解决这样的错误:
const char* good_parens = R"xyz()")xyz";
可以构造源字符串文字(没有转义字符),包括源中的换行符:
// represents the string: hello
//goodbye
const wchar_t* newline = LR"(hello
goodbye)";
std::文本 (C++14)
std:: 是用户定义文字的标准库实现(见下文),表示为“xyz”(带有 s 后缀)。 此字符串文字生成 std::、std::、std:: 或 std:: 类型的临时对象,具体取决于指定的前缀。 当不带任何前缀使用时,如上所示,将生成 std:: 。 L"xyz"s 产生 std::。 u“xyz”生成,U“xyz”生成。
//#include
//using namespace std::string_literals;
string str{ "hello"s };
string str2{ u8"Hello World" }; // Before C++20
u8string u8str2{ u8"Hello World" }; // C++20
wstring str3{ L"hello"s };
u16string str4{ u"hello"s };
u32string str5{ U"hello"s };
s 后缀也可用于原始字符串:
u32string str6{ UR"(She said "hello.")"s };
std:: 在头文件的命名空间 std:::: 中定义。 由于 std:::: 和 std:: 都被声明为内联命名空间,因此 std:::: 会自动被视为直接属于命名空间 std。
字符串字面量大小
对于 ANSI char* 字符串和其他单字节编码(但不是 UTF-8),字符串的大小(以字节为单位)是字符数加 1(对于空终止字符)。 对于所有其他字符串类型,大小与字符数并不严格相关。 UTF-8 使用最多四个 char 元素对某些代码单元进行编码,或者可以像 UTF-16 编码一样使用两个元素(总共四个字节)对单个“代码单元”进行编码。 此示例演示了宽字符串文字的大小(以字节为单位):
const wchar_t* str = L"Hello!";
const size_t byteSize = (wcslen(str) + 1) * sizeof(wchar_t);
请注意,() 和 () 不包括空终止字符的大小,它等于字符串类型的元素大小:char* 或 * 字符串中的一个字节,* 或 * 字符串部分中的两个单词,*字符串中的四个字节。
在 2022 年版本 17.0 之前的版本中,字符串文字的最大长度为 65,535 字节。 此限制适用于窄字符串和宽字符串文字。 在 2022 年版本 17.0 及更高版本中,此限制被取消,字符串长度受可用资源限制。
修改字符串文字
由于字符串(不包括 std::)是常量,因此尝试修改它们(例如 str[2] = 'A')将导致编译器错误。
投入的
在 C++ 中,可以使用字符串文字来初始化指针,使指针成为非常量 char 或 . 此非常量初始化在 C99 代码中可用,但在 C++98 中已弃用并在 C++11 中删除。 尝试修改此字符串将导致访问冲突,例如:
wchar_t* str = L"hello";
str[2] = L'a'; // run-time error: access violation
当您设置 /Zc:(禁用字符串文字类型转换)编译器选项并且字符串文字转换为非常量 char 指针时,可能会导致编译器错误。 我们推荐将其用于符合标准的可移植代码。 使用 auto 关键字声明字符串文字初始化的指针也是一种很好的做法,因为它解析为正确的(常量)类型。 例如,此代码示例捕获在编译时写入字符串文字的尝试:
auto str = L"hello";
str[2] = L'a'; // C3892: you cannot assign to a variable that is const.
在某些情况下,可以组合相同的字符串文字,从而节省可执行文件中的空间。 在字符串文字合并期间,编译器将使对特定字符串文字的所有引用都指向内存中的同一位置,而不是对单独的字符串文字实例的每个引用。 要启用字符串合并,请使用 /GF 编译器选项。
“特定于”部分就结束了。
连接相邻的字符串文字
相邻的宽或窄字符串文字被连接起来。 声明如下:
char str[] = "12" "34";