Tip
本文屬於 基礎部分, 適合已經至少懂一種程式語言並正在學習 C# 的開發者。 如果你是程式新手,建議先從 入門 教學開始。
你是從 Java 還是 C++ 轉過來? C# string 是一種以 String 為基礎的不可變參考類型。 UTF-16 是記憶體內編碼,類似於 Java 的 String。 與 C/C++ 不同,字串沒有空終止,也不會衰減成指標。
字串是一連串的字元。 在 C# 中,string 是 System.String 類型的語言關鍵字。 你寫的每個字串文字都會產生一個 System.String 實例。
string 與 String
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,就無法更改它的字元。 像是 ToUpperInvariant、Replace、Substring 和 Trim 這類方法,會傳回一個包含修改後值的新字串。 原本的實例保持不變。
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 | 依分隔符將字串拆分為子字串 |
| 連接 | 串聯字串 — +、插值、Concat、Join |
| Modify | 產生一個轉換後的副本 — Replace, Trim, Substring |
| 比較 | 使用正確的 StringComparison 測試相等性與排序 |
完整的 API 介面——每個多載、每個方法——都記載於 String 參考文件中。