C# 字串

Tip

本文屬於 基礎部分, 適合已經至少懂一種程式語言並正在學習 C# 的開發者。 如果你是程式新手,建議先從 入門 教學開始。

你是從 Java 還是 C++ 轉過來? C# string 是一種以 String 為基礎的不可變參考類型。 UTF-16 是記憶體內編碼,類似於 Java 的 String。 與 C/C++ 不同,字串沒有空終止,也不會衰減成指標。

字串是一連串的字元。 在 C# 中,stringSystem.String 類型的語言關鍵字。 你寫的每個字串文字都會產生一個 System.String 實例。

stringString

string關鍵字與String類型名稱指的是同一類型。 它們編譯成相同的中級語言(IL)。

// The 'string' keyword is an alias for System.String. The two are identical.
string a = "hello";
String b = "hello";

Console.WriteLine(a == b);                // True
Console.WriteLine(typeof(string) == typeof(String)); // True

在你自己的程式碼中偏好使用關鍵字 string 。 它與其他內建的類型關鍵字(,intbool , )double相容,且無需指令using System;即可運作。

字串是不可變的

不可變 的意思是數值在建立後無法更改。 一旦你建立了 string,就無法更改它的字元。 像是 ToUpperInvariantReplaceSubstringTrim 這類方法,會傳回一個包含修改後值的字串。 原本的實例保持不變。

string greeting = "hello";

// ToUpper returns a *new* string. The original is unchanged.
string shouted = greeting.ToUpperInvariant();

Console.WriteLine(greeting);  // hello
Console.WriteLine(shouted);   // HELLO

因為字串是不可變的,你可以安全地在方法和執行緒間共享。 這種不變性解釋了為何 string 該型別在日常使用中表現得像值型,儘管它是參考型態。

當你在迴圈中用許多小片段建立字串時,請使用 StringBuilder 就地附加,並在最後產生單一字串:

// For many sequential edits, StringBuilder avoids allocating a new string each time.
var builder = new StringBuilder();
for (int i = 1; i <= 3; i++)
{
    builder.Append("item ").Append(i).Append(';');
}
string result = builder.ToString();
Console.WriteLine(result);    // item 1;item 2;item 3;

字串常值

C# 提供四種字面形式。 每種形式適合不同的內容。 作為快速指南:

  • 對於簡短、簡單且最多只有少數幾個跳脫序列的文字,請使用 一般常值
  • 當內容以反斜線為主,例如Windows路徑或正則表達式模式時,請使用逐字文字
  • 對於多行或具結構格式的文字,例如內嵌 JSON、SQL、XML 或已格式化的訊息區塊,請使用 原始字串常值
  • 在上述任何字面上加 $ 一個前綴,當你需要嵌入值時,就能得到 插值字串
  • 在字面值後加上 u8 後綴,即可產生 UTF-8 位元組序列(a ReadOnlySpan<T>byte),用於位元組導向 API。 詳情請參閱語言參考中的 UTF-8 字串文字

正則文字與跳脫序列

一個正則字串的字面值會用雙引號包住。 反斜線會啟動逃脫序列:

// Common escape sequences inside a regular string literal.
string tabbed   = "name:\tAda";          // \t  tab
string twoLines = "line 1\nline 2";      // \n  newline
string quoted   = "She said \"hi\".";    // \"  literal quote
string path     = "C:\\src\\app";        // \\  literal backslash

Console.WriteLine(tabbed);
Console.WriteLine(twoLines);
Console.WriteLine(quoted);
Console.WriteLine(path);

常見的跳脫序列包括 \n (換行)、 \t (tab)、 \" (字面引號)、 \\ (字面反斜線)、 \0 (空字符)以及 Unicode 跳脫(\uXXXX\UXXXXXXXX)。

從 C# 13 開始, \e 代表 ESC 控制字元(U+001B)。 這是 ANSI 終端跳脫序列的起始位元組:

// Beginning in C# 13, \e represents the ESC control character (U+001B).
// It's used to start ANSI terminal escape sequences.
string esc = "\e[31mError\e[0m: file missing";

Console.WriteLine(esc);                  // ESC[31mError ESC[0m: file missing
Console.WriteLine((int)'\e');          // 27

當文字較短且只包含少數跳脫序列時,使用規則字面文字。 當逃脫人數開始超過可見角色時,切換成逐字或直接字面。

逐字常值

逐字字面值前面會加上 @。 反斜線會按字面值處理,這對 Windows 路徑和正規表示式模式非常有用:

// A verbatim string literal (@) treats backslashes literally.
// Useful for Windows paths and regular expressions.
string winPath = @"C:\src\app\readme.md";
string pattern = @"\d{3}-\d{4}";

Console.WriteLine(winPath);
Console.WriteLine(pattern);

若要在逐字字串中嵌入字面引用,將它加倍: @"She said ""hi""."。 逐字字串也可以跨越多條實體線。

當內容中有反斜線但內嵌引言不多時,逐字直譯是正確的選擇。 對於多行文字或帶有引號的內容,原始字串字面值通常會更清楚。

原始字串常值

對於任何包含引號、反斜線或多行內容的字面值,建議優先使用 原始字串字面值。 它們完全消除逃逸雜訊,使它們最適合用於內嵌 JSON、SQL、XML、REGEX 模式,以及格式化的文字區塊,因為原始碼應該看起來像輸出:

// Raw string literals use three or more quotes and need no escaping.
// The source looks like the output, which is ideal for inline JSON, SQL, XML, and the like.
string json = """
    {
        "name": "Ada",
        "roles": ["admin", "editor"]
    }
    """;

string sql = """
    SELECT Id, Name
    FROM   Users
    WHERE  Name = 'O''Brien'
    """;

Console.WriteLine(json);
Console.WriteLine(sql);

原始字串字面幾乎可以消除跳脫序列,並能配合你需要的格式和引用。 完整規則請參見 原始字串字面值

插值字串

$ 綴會將文字變成 插值字串。 洞中的 {} 表達式會被評估並插入結果,你可以在洞內套用標準格式的指定符和對齊。 插值也可以與其他常值形式結合使用——使用 $@"..." 來插入逐字常值,或使用 $"""...""" 來插入原始字串常值,以產生具有豐富格式的輸出:

// The $ prefix evaluates expressions inside { } and inserts their values.
string name = "Ada";
int score = 92;

string greeting = $"Hello, {name}! Your score is {score}.";
// Format specifiers and alignment work inside the holes.
string formatted = $"pi = {Math.PI:F3}, padded = |{name,10}|";

// Combine $ and """ for richly formatted multiline output.
string report = $"""
    Report for {name}
    -----------------
    Score : {score}
    Grade : {(score >= 90 ? "A" : "B")}
    """;

Console.WriteLine(greeting);
Console.WriteLine(formatted);
Console.WriteLine(report);

插值是日常程式碼中從數值組合字串的推薦方法。

索引與 char

A string 是一串 UTF-16 編碼單元。 索引器回傳一個 System.Char,代表單一的 UTF-16 碼單元,不一定是完整的 Unicode 碼點。 Length 回傳代碼單位數。

// A string is a sequence of UTF-16 code units. s[i] returns one char.
string word = "café";

Console.WriteLine(word.Length);          // 4
Console.WriteLine(word[0]);              // c

foreach (char c in word)
{
    Console.Write($"{c} ");              // c a f é
}
Console.WriteLine();

對於可能包含表情符號或基本多文種平面以外字元的文字,請使用 rune 搭配 Rune 逐一處理,或使用 StringInfo 按字素叢集逐一處理。 純 char 迭代適用於大多數西方文本及以 ASCII 為主的內容。

字串相等

string 進行相等比較時,比較的是字元序列,而非參考是否相同:

// String equality compares character sequences, not references.
string left  = "hello";
string right = string.Concat("hel", "lo");

Console.WriteLine(left == right);                                      // True
Console.WriteLine(left.Equals(right, StringComparison.Ordinal));       // True

對於需要區域感知或案例感知的比較,請傳遞明確 StringComparison 的數值。 請使用 StringComparison.Ordinal 表示通訊協定值、識別碼及其他非語言文字。

常見的字串操作

請參考以下表格作為 C# 日常字串操作的快速指南:

類別 內容涵蓋
搜尋 尋找字元或子字串,測試前綴與後綴
Split 依分隔符將字串拆分為子字串
連接 串聯字串 — +、插值、ConcatJoin
Modify 產生一個轉換後的副本 — ReplaceTrimSubstring
比較 使用正確的 StringComparison 測試相等性與排序

完整的 API 介面——每個多載、每個方法——都記載於 String 參考文件中。

參見