C++ 支援各種字串和字元類型,並提供方法來表示所有這些類型的常值。 在原始程式碼中,您可以使用字元集表示字元和字串常值的內容。 通用字元名稱和逸出字元允許您只使用基本來源字元集表示任何字串。 原始字串常值可讓您避免使用逸出字元,而且可用來表示所有類型的字串常值。 您也可以建立 std::string
常值,而不需要執行額外的建構或轉換步驟。
#include <string>
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
}
字串常值可以沒有前置詞,也可以有 u8
、 L
、 u
和 U
這些前置詞,分別表示窄字元(單字節或多字節)、UTF-8、寬字元(UCS-2 或 UTF-16)、UTF-16 和 UTF-32 編碼。 原始字串常值可以有 R
、u8R
、LR
、uR
和 UR
前置碼,用於這些編碼的原始版本等價。 若要建立暫存或靜態 std::string
值,您可以使用字串常值或原始字串常值搭配 s
後綴。 如需詳細資訊,請參閱 下面的字串常值 一節。 如需基本來源字元集、通用字元名稱和使用原始碼中擴充代碼頁字元的詳細資訊,請參閱 字元集。
字元字面值
「字元常值」 (character literal) 是由常數字元所組成。 它以單引號括住的字符來表示。 字元常值有五種:
一般字元常值,類型為
char
,例如'a'
UTF-8 字元常值類型
char
(在 C++20 中為char8_t
),例如u8'a'
。wchar_t
類型的寬字元常值,例如L'a'
UTF-16字元常值類型為
char16_t
,例如u'a'
UTF-32 字元常值,其類型為
char32_t
,例如U'a'
用於字元常值的字元可以是任何字元,但保留的字元反斜杠 (\
)、單引號 ('
) 或換行符 () 除外。 使用逸出序列可指定保留字元。 只要類型大到足以容納字元,就可以使用通用字元名稱指定字元。
編碼方式
字元常值會根據其前置詞以不同的方式編碼。
不含前置詞的字元常值是一般字元常值。 包含單一字元、逸出序列或通用字元名稱的一般字元常值,可在執行字元集中表示的值等於執行字元集中編碼的數值。 包含多個字元、逸出序列或通用字元名稱的一般字元常值是 多重字元常值。 多重字元常值或無法在執行字元集中表示的一般字元常值具有 類型
int
,且其值為實作定義。 如需 MSVC,請參閱 下方Microsoft特定 章節。開頭為
L
前綴的字元字面量是寬字元字面量。 包含單一字元、逸出序列或通用字元名稱之寬字元常值的值,與執行寬字元集中編碼的數值相等,除非字元常值在執行寬字元集中沒有表示法,在此情況下,該值是實作定義的。 包含多個字元、跳脫序列或通用字元名稱之寬字元常值的值是實作定義的。 如需 MSVC,請參閱 下方Microsoft特定 章節。開頭是
u8
字首的字符常量是 UTF-8 字符常量。 包含單一字元、逸出序列或通用字元名稱之 UTF-8 字元常值的值,如果可由單一 UTF-8 代碼單位表示,則其值等於其 ISO 10646 字碼點值(對應至 C0 Controls 和 Basic Latin Unicode 區塊)。 如果值不能以單一 UTF-8 程式代碼單位表示,則程式格式不正確。 UTF-8 字元常值,其中包含一個以上的字元、跳脫字元序列或通用字元名稱,格式錯誤。以
u
為前綴的字元常值是 UTF-16 字元常值。 包含單一字元、逸出序列或通用字元名稱之 UTF-16 字元常值的值,如果可由單一 UTF-16 代碼單位表示,則其值等於其 ISO 10646 字碼點值(對應至基本多語平面)。 如果值不能以單一 UTF-16 程式代碼單位表示,則程式格式不正確。 UTF-16 字元常值,包含一個以上的字元、逸出序列或通用字元名稱的格式不正確。以
U
為前置的字元常值是 UTF-32 編碼的字元常值。 包含單一字元、轉義序列或通用字元名稱的 UTF-32 字元常值,其值等於其 ISO 10646 字碼點值。 UTF-32 字元常值,包含一個以上的字元、逸出序列或通用字元名稱的格式不正確。
逸出序列
逸出序列有三種:簡單的、八進位和十六進位。 逸出序列可以是下列任何一個值:
值 | 逸出序列 |
---|---|
換行 | \n |
反斜線 | \\ |
水平定位 | \t |
問號 | ? 或\? |
垂直製表符 | \v |
單引號 | \' |
退格鍵 | \b |
雙引號 | \" |
回車 | \r |
null 字元 | \0 |
換頁指令 | \f |
八進位 | \ooo |
警示 (鈴聲) | \a |
十六進位 | \xhhh |
八進位逸出序列是由反斜線後接著一到三個八進位數字組成的序列。 八進位逸出序列會在第一個非八進位數位的字元終止,如果在第三個數字之前遇到這樣的字元。 最高可能的八進位值為 \377
。
十六進位逸出序列是反斜杠,後面接著字元 x
,後面接著一或多個十六進位數位序列。 系統會忽略前置零。 在普通或 u8 前置的字元字面量中,最高的十六進位值為 0xFF。 在以 L 或 u 為前綴的寬字元字面值中,最高的十六進位值是 0xFFFF。 在帶有 U 前綴的寬字元字面值中,最高的十六進位值是 0xFFFFFFFF。
此範例程式代碼示範一些使用一般字元常值的逸出字元範例。 其他字元文字類型的逸出序列語法也是有效的。
#include <iostream>
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;
}
/* Output:
Newline character:
ending
Tab character: ending
Backspace character:ending
Backslash character: \ending
Null character: ending
*/
反斜杠字元(\
),位於行尾時,為行續接符號。 如果您想要讓反斜線字元顯示為字元常量,您必須輸入連續的兩個反斜線 (\\
)。 如需行接續字元的詳細資訊,請參閱 Phases of Translation。
Microsoft 專用
若要從窄的多字元字面值建立一個值,編譯器會將單引號之間的字元或字元序列轉換成32位整數中的8位元值。 字面量中的多個字元,會根據需要從高序位到低序位填入對應的位元組。 然後,編譯程式會將整數轉換成遵循一般規則的目的地類型。 例如,若要建立 char
值,編譯程式會採用低序位元組。 若要建立 wchar_t
或 char16_t
值,編譯器會採用低序位字組。 如果任何位元設定超過指定的位元組或字節,編譯器會警告結果會被截斷。
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-decimal':字元太大。
看似同時具有十六進位與非十六進位字元的逸出序列,會被評估為一個多字元常值,其中包含一段十六進位逸出序列(直到最後一個十六進位字元),接著是非十六進位字元。 不包含十六進位數位的十六進位逸出序列會導致編譯程序錯誤 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
Microsoft 特定 區段在這裡結束。
通用字元名稱
在字元常值和原生 (非原始) 的字串常值中,任何字元都可能使用通用字元名稱表示。 通用字元名稱由前綴 \U
開始,後接八位數的 Unicode 碼位,或由前綴 \u
開始,後接四位數的 Unicode 碼位。 所有八位數或四位數的字元必須出現,才能組成格式正確的通用字元名稱。
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 中的值。 至於 Unicode 代理對,請使用 \UNNNNNNNN
來指定通用字元名稱,其中 NNNNNNNN 是字元的八位數字碼。 如有必要,編譯程式會產生 Surrogate 配對。
在 C++03 中,語言只允許其通用字元名稱來表示字元子集,並允許某些實際上不代表任何有效 Unicode 字元的通用字元名稱。 這個錯誤已在 C++11 標準中修正。 在 C++11 中,字元和字串常值及識別項可以使用通用字元名稱。 如需通用字元名稱的詳細資訊,請參閱 Character Sets。 如需 Unicode 的詳細資訊,請參閱 Unicode。 如需 Surrogate 字組的詳細資訊,請參閱 Surrogate 字組和補充字元。
字串字面值
字串常值代表構成以 null 結束的字串的一連串字元。 這些字元必須使用雙引號括起來。 有下列字串字面值類型:
窄字串常值
字串文字是一個不帶前置詞的雙引號分隔、以 Null 結束的陣列,類型為 const char[n]
,其中 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 引進可攜 char8_t
式 (UTF-8 編碼的 8 位 Unicode) 字元類型。 在 C++20 中,u8
常值前置詞會指定 char8_t
字元或字串,而不是 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 結尾的常數 wchar_t
陣列,其前面加上 'L
',並且包含除了雙引號("
)、反斜杠(\
)或換行符以外的任何圖形字元。 寬字串字面值可以包含上述的跳脫序列,以及任何通用字元名稱。
const wchar_t* wide = L"zyxw";
const wchar_t* newline = L"hello\ngoodbye";
char16_t 和 char32_t (C++11)
C++11 引進可攜式 char16_t
(16 位元 Unicode) 和 char32_t
(32 位元 Unicode) 字元類型:
auto s3 = u"hello"; // const char16_t*
auto s4 = U"hello"; // const char32_t*
原始字串常值 (C++11)
原始字串字面值是任何字元類型的 Null 終止陣列,其中包含各種圖形符號,包括雙引號 ("
)、反斜線 (\
)或換行符。 原始字串文本通常用於使用字元類別的正則表達式,以及 HTML 字串和 XML 字串。 如需範例,請參閱下列文章: Bjarne Stroustrup 的 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::string 常值 (C++14)
std::string
字面值是標準函式庫對使用者定義字面值的實作(請參閱下方),其表示為 "xyz"s
(有 s
字尾)。 這種字面量會根據指定的前置詞,產生類型為 std::string
、std::wstring
、std::u32string
或 std::u16string
的暫存物件。 未使用前置詞時,如上所示, std::string
會產生 。
L"xyz"s
會產生 std::wstring
。
u"xyz"s
產生一個std::u16string,並U"xyz"s
產生一個std::u32string。
//#include <string>
//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::string
常值定義在 std::literals::string_literals
命名空間中,位於 <string> 標頭檔案中。 因為 std::literals::string_literals
,而且 std::literals
都宣告為 內嵌命名空間,所以會自動將 std::literals::string_literals
視為直接屬於命名空間 std
。
字串常值的大小
對於 ANSI char*
字串和其他單一位元組編碼(但不是 UTF-8),字串字面量的大小(以位元組為單位)是字元數加上 1,以包括終止的 null 字元。 對於所有其他字串類型,大小並不嚴格地與字元數目相關。 UTF-8 最多使用四 char
個元素來編碼某些 程式代碼單位, char16_t
或 wchar_t
編碼為 UTF-16 可能會使用兩個元素(總共四個字節)來編碼單 一程式代碼單位。 本例顯示全形字串常值以位元組為單位的大小:
const wchar_t* str = L"Hello!";
const size_t byteSize = (wcslen(str) + 1) * sizeof(wchar_t);
請注意,strlen()
和 wcslen()
不包含終止 Null 字元的大小,其大小等於字串類型的元素大小:char*
和 char8_t*
字串為一個位元組,wchar_t*
和 char16_t*
字串為兩個位元組,而 char32_t*
字串為四個位元組。
串連之後字串文字的最大長度:
- 2022 17.0 版之前的 Visual Studio:串連後的字串常值長度上限為 65,535 個字節。 這適用於窄字串和寬字串的字面值。
- 從 Visual Studio 2022 17.0 版起:串連之後字串常值的最大長度只會受限於可用的記憶體。 不過,串連之前的大小限制仍為16,384個字節
修改字串字面值
因為字串常值(不包括 std::string
常值)是常數,因此嘗試修改它們,例如, str[2] = 'A'
會導致編譯程序錯誤。
微軟特有
在 Microsoft C++ 中,您可以使用常值字串來初始化非 const char
或 wchar_t
的指標。 C99 程式代碼中允許此非 const 初始化,但在 C++98 中已被取代,並在 C++11 中移除。 嘗試修改字串造成存取違規,如此範例所示:
wchar_t* str = L"hello";
str[2] = L'a'; // run-time error: access violation
當您設定 /Zc:strictStrings
[停用字串常值類型轉換] 編譯器選項時,會導致在字串常數被轉換為非常數字元指標時發出錯誤。 我們建議它用於符合標準的可攜式程序代碼。 使用 auto
關鍵詞來宣告以字串常值初始化的指標也是很好的做法,因為它能轉換為正確的(const)類型。 例如,這個程式碼範例會在編譯時捕捉嘗試寫入字串常值的動作:
auto str = L"hello";
str[2] = L'a'; // C3892: you cannot assign to a variable that is const.
在某些情況下,可以結合相同字串常值來節省可執行檔中的空間。 在字串常值共用中,編譯器會讓特定字串常值的所有參考指向記憶體內部相同位置,而不是讓每個參考都指向字串常值的個別執行個體。 若要啟用字串共用,請使用 /GF
編譯程序選項。
Microsoft 特定 區段在這裡結束。
串連相鄰的字串常值
相鄰的寬或窄的字串文字會被串接在一起。 這個宣告:
char str[] = "12" "34";
等同於這份宣告:
char atr[] = "1234";
以及這個宣告:
char atr[] = "12\
34";
使用內嵌十六進位逸出程式碼指定字串常值可能造成未預期的結果。 下列範例要建立一個包含 ASCII 符號 5 的字串文字,後面接著符號 f、i、v 和 e:
"\x05five"
實際結果是十六進位 5F,即底線字元的 ASCII 碼,後面接著字母 "i"、"v"、"e"。 若要取得正確的結果,您可以使用下列其中一個逸出序列:
"\005five" // Use octal literal.
"\x05" "five" // Use string splicing.
std::string
常值(以及相關的std::u8string
、std::u16string
和std::u32string
)可以與針對basic_string
型別定義的+
運算符串連。 它們的串接方式也與相鄰字串字面值相同。 在這兩種情況下,字串編碼和後置詞必須相符:
auto x1 = "hello" " " " world"; // OK
auto x2 = U"hello" " " L"world"; // C2308: disagree on prefix
auto x3 = u8"hello" " "s u8"world"z; // C3688, disagree on suffixes
具有通用字元名稱的字串常值
原生 (非原始) 的字串常值可以使用通用字元名稱來代表任何字元,只要通用字元名稱可以編碼為字串類型中的一或多個字元。 例如,代表擴充字元的通用字元名稱不能使用 ANSI 代碼頁在窄字串中編碼,但可以在某些多位元組代碼頁、UTF-8 字串或寬字元串中以窄字元串編碼。 在 C++11 中,Unicode 支援會由 char16_t*
和 char32_t*
字串類型延伸,C++20 會將它擴充至 char8_t
類型:
// ASCII smiling face
const char* s1 = ":-)";
// UTF-16 (on Windows) encoded WINKING FACE (U+1F609)
const wchar_t* s2 = L"😉 = \U0001F609 is ;-)";
// UTF-8 encoded SMILING FACE WITH HALO (U+1F607)
const char* s3a = u8"😇 = \U0001F607 is O:-)"; // Before C++20
const char8_t* s3b = u8"😇 = \U0001F607 is O:-)"; // C++20
// UTF-16 encoded SMILING FACE WITH OPEN MOUTH (U+1F603)
const char16_t* s4 = u"😃 = \U0001F603 is :-D";
// UTF-32 encoded SMILING FACE WITH SUNGLASSES (U+1F60E)
const char32_t* s5 = U"😎 = \U0001F60E is B-)";