Share via


字串和字元常值 (C++)

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
}

字串常值不能有前置詞、或 u8LuU 前置詞,分別表示窄字元(單一位元組或多位元組)、UTF-8、寬字元(UCS-2 或 UTF-16)、UTF-16 和 UTF-32 編碼。 原始字串常值可以有 Ru8RLRuRUR 前置詞,這些編碼的原始版本對等專案。 若要建立暫存或靜態 std::string 值,您可以使用字串常值或原始字串常值搭配 s 尾碼。 如需詳細資訊,請參閱 下面的字串常值 一節。 如需基本來源字元集、通用字元名稱和使用原始程式碼中擴充字碼頁字元的詳細資訊,請參閱 字元集

字元常值

「字元常值」 (character literal) 是由常數字元所組成。 其以以單引號括住的字元表示。 字元常值有五種:

  • 類型的 char 一般字元常值,例如 'a'

  • 例如,類型 char 為 UTF-8 字元常值 ( char8_t 在 C++20 中), u8'a'

  • wchar_t類型的全形字元常值,例如 L'a'

  • 類型的 char16_t UTF-16 字元常值,例如 u'a'

  • 類型的 char32_t UTF-32 字元常值,例如 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_tchar16_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 字碼點,或後面接著四位數 Unicode 字碼點的前置 \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'

Surrogate 字組

通用字元名稱無法在 Surrogate 字碼點範圍 D800-DFFF 中編碼值。 至於 Unicode surrogate 字組,請使用 \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::wstringstd::u32stringstd::u16string 的暫存物件。 未使用前置詞時,如上所示, std::string 會產生 。 L"xyz"sstd::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 中。 因為 std::literals::string_literals,而且 std::literals 都宣告為 內嵌命名空間,所以會自動將 std::literals::string_literals 視為直接屬於命名空間 std

字串常值的大小

對於 ANSI char* 字串和其他單一位元組編碼(但不是 UTF-8),字串常值的大小(以位元組為單位)是終止 Null 字元的字元數加上 1。 對於所有其他字串類型,大小並不嚴格地與字元數目相關。 UTF-8 最多使用四 char 個元素來編碼某些 程式碼單位 char16_twchar_t 編碼為 UTF-16 可能會使用兩個元素(總共四個位元組)來編碼單 一程式碼單位 。 本例顯示全形字串常值以位元組為單位的大小:

const wchar_t* str = L"Hello!";
const size_t byteSize = (wcslen(str) + 1) * sizeof(wchar_t);

請注意, strlen()wcslen() 不包含終止 Null 字元的大小,其大小等於字串類型的元素大小:或 char8_t* 字串上的一個位元組 char* 、或 字串上的兩個位元組 wchar_t*char16_t* ,以及字串上的 char32_t* 四個位元組。

在 Visual Studio 2022 17.0 版之前的 Visual Studio 版本中,字串常值的最大長度為 65,535 個位元組。 這個限制適用於半形字串常值和全形字串常值。 在 Visual Studio 2022 17.0 版和更新版本中,此限制隨即提升,字串長度受限於可用的資源。

修改字串常值

因為字串常值(不包括 std::string 常值)是常數,因此嘗試修改它們,例如, str[2] = 'A' 會導致編譯器錯誤。

Microsoft 專有

在 Microsoft C++ 中,您可以使用字串常值來初始化非 const charwchar_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常值 (以及相關的 、 和 ) 可以與針對 basic_string 型別定義的運算子串連 +std::u32stringstd::u16stringstd::u8string 它們的串連方式也與相鄰字串常值相同。 在這兩種情況下,字串編碼和後置詞必須相符:

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-)";

另請參閱

字元集
數值、布林值和指標常值
使用者定義的常值