字串和字串常值

字串是 String 類型的物件,其值為文字。 就內部而言,文字會儲存為 Char 物件的循序唯讀集合。 C# 字串結尾沒有 Null 終止字元;因此,C# 字串可以包含任意數目的內嵌 Null 字元, ('\0') 。 字串的 Length 屬性代表它包含的 Char 物件數目,而非 Unicode 字元的數目。 若要存取字串中的個別 Unicode 字碼指標,請使用 StringInfo 物件。

string 與 System.String

在 C# 中,string 關鍵字是 String 的別名。 因此, Stringstring 是相等的,不論建議使用提供的別名 string ,即使沒有 using System; 也一樣。 String 類別提供許多方法來安全地建立、操作和比較字串。 此外,C# 語言會多載一些運算子,以簡化常見的字串作業。 如需關鍵字的詳細資訊,請參閱字串。 如需類型和其方法的詳細資訊,請參閱 String

宣告和初始化字串

您可以透過多種方式宣告並初始化字串,如下列範例所示:

// Declare without initializing.
string message1;

// Initialize to null.
string message2 = null;

// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;

// Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";

// Initialize with a verbatim string literal.
string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0";

// Use System.String if you prefer.
System.String greeting = "Hello World!";

// In local variables (i.e. within a method body)
// you can use implicit typing.
var temp = "I'm still a strongly-typed System.String!";

// Use a const string to prevent 'message4' from
// being used to store another string value.
const string message4 = "You can't get rid of me!";

// Use the String constructor only when creating
// a string from a char*, char[], or sbyte*. See
// System.String documentation for details.
char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);

除了使用字元陣列初始化字串時,您不會使用 新的 運算子來建立字串物件。

使用 Empty 常數值初始化字串,以建立字串長度為零的新 String 物件。 零長度字串的字串常值表示法是 ""。 使用 Empty 值初始化字串,而非 null,即可降低發生 NullReferenceException 的機會。 使用靜態 IsNullOrEmpty(String) 方法,先驗證字串的值,再嘗試進行存取。

字符串的不可变性

字串物件是不 可變的:建立字串物件之後便無法變更。 所有看似會修改字串的 String 方法和 C# 運算子,實際上會以新的字串物件傳回結果。 在下列範例中,當 s1s2 的內容串連以組成單一字串時,兩個原始字串將不會被修改。 += 運算子會建立新的字串,其中包含結合的內容。 新的物件會指派給變數 s1,而先前指派給 s1 的原始物件將會被釋放以進行記憶體回收,因為已經沒有其他具有其參考的變數。

string s1 = "A string is more ";
string s2 = "than the sum of its chars.";

// Concatenate s1 and s2. This actually creates a new
// string object and stores it in s1, releasing the
// reference to the original object.
s1 += s2;

System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.

因為對字串的「修改」實際上是建立新的字串,當您建立對字串的參考時,必須特別謹慎。 如果您建立對字串的參考,然後「修改」原始字串,該參考將會繼續指向原始物件,而非修改字串時所建立的新物件。 下列程式碼說明這個行為:

string str1 = "Hello ";
string str2 = str1;
str1 += "World";

System.Console.WriteLine(str2);
//Output: Hello

如需如何根據修改建立新字串的詳細資訊,例如搜尋和取代原始字串上的作業,請參閱 如何修改字串內容

引號字串常值

引號字串常值 是以單引號字元開頭和結尾, (") 在同一行。 引號字串常值最適合用於符合單行且不包含任何 逸出序列的字串。 引號字串常值必須內嵌逸出字元,如下列範例所示:

string columns = "Column 1\tColumn 2\tColumn 3";
//Output: Column 1        Column 2        Column 3

string rows = "Row 1\r\nRow 2\r\nRow 3";
/* Output:
    Row 1
    Row 2
    Row 3
*/

string title = "\"The \u00C6olean Harp\", by Samuel Taylor Coleridge";
//Output: "The Æolean Harp", by Samuel Taylor Coleridge

逐字字串常值

逐字字串常值 對於多行字串、包含反斜線字元或內嵌雙引號的字串更為方便。 逐字字串會保留新行字元做為字串文字的一部分。 使用雙引號在逐字字串中內嵌引號。 下列範例示範一些逐字字串的常見用法︰

string filePath = @"C:\Users\scoleridge\Documents\";
//Output: C:\Users\scoleridge\Documents\

string text = @"My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...";
/* Output:
My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...
*/

string quote = @"Her name was ""Sara.""";
//Output: Her name was "Sara."

原始字串常值

從 C# 11 開始,您可以使用 原始字串常值 ,更輕鬆地建立多行的字串,或使用任何需要逸出序列的字元。 原始字串常值 不需要使用逸出序列。 您可以撰寫字串,包括空白字元格式設定,您希望它在輸出中顯示的方式。 原始字串常值

  • 以至少三個雙引號字元序列開頭和結尾, (""") 。 您可以允許三個以上的連續字元開始和結束序列,以支援包含三個 (或多個) 重複引號字元的字串常值。
  • 單行原始字串常值需要同一行的開頭和結尾引號字元。
  • 多行原始字串常值需要其本身行的開頭和結尾引號字元。
  • 在多行原始字串常值中,會移除結尾引號左邊的任何空白字元。

下列範例示範這些規則:

string singleLine = """Friends say "hello" as they pass by.""";
string multiLine = """
    "Hello World!" is typically the first program someone writes.
    """;
string embeddedXML = """
       <element attr = "content">
           <body style="normal">
               Here is the main text
           </body>
           <footer>
               Excerpts from "An amazing story"
           </footer>
       </element >
       """;
// The line "<element attr = "content">" starts in the first column.
// All whitespace left of that column is removed from the string.

string rawStringLiteralDelimiter = """"
    Raw string literals are delimited 
    by a string of at least three double quotes,
    like this: """
    """";

下列範例示範根據這些規則所報告的編譯器錯誤:

// CS8997: Unterminated raw string literal.
var multiLineStart = """This
    is the beginning of a string 
    """;

// CS9000: Raw string literal delimiter must be on its own line.
var multiLineEnd = """
    This is the beginning of a string """;

// CS8999: Line does not start with the same whitespace as the closing line
// of the raw string literal
var noOutdenting = """
    A line of text.
Trying to outdent the second line.
    """;

前兩個範例無效,因為多行原始字串常值需要自己的行的開頭和結尾引號序列。 第三個範例無效,因為文字會從右引號序列中取出。

當您產生包含使用引號字串常值或逐字字串常值時需要逸出 序列 字元的文字時,您應該考慮原始字串常值。 原始字串常值會讓您和其他人更容易閱讀,因為它會更類似輸出文字。 例如,請考慮下列包含格式化 JSON 字串的程式碼:

string jsonString = """
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": 20,
      "Low": -10
    },
    "Hot": {
      "High": 60,
      "Low": 20
    }
            },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}
""";

比較該文字與 JSON 序列化範例中的對等文字,這不會利用這項新功能。

字串逸出序列

逸出序列 字元名稱 Unicode 編碼
\' 單引號 0x0027
\" 雙引號 0x0022
\\ 反斜線 0x005C
\0 Null 0x0000
\a 警示 0x0007
\b 退格鍵 0x0008
\f 換頁字元 0x000C
\n 新行 0x000A
\r 歸位字元 0x000D
\t 水平 Tab 鍵 0x0009
\v 垂直 Tab 鍵 0x000B
\u Unicode 逸出序列 (UTF-16) \uHHHH (範圍:0000 - FFFF;example: \u00E7 = 「ç」)
\U Unicode 逸出序列 (UTF-32) \U00HHHHHH (範圍:000000 - 10FFFF;example: \U0001F47D = 「 👽 」)
\x 類似 "\u" (除了變數長度之外) 的 Unicode 逸出序列 \xH[H][H][H] (範圍:0 - FFFF;範例: \x00E7\x0E7\xE7 = 「ç」)

警告

當使用 \x 逸出序列且指定的十六進位數字少於 4 個時,若尾隨在逸出序列之後的字元是有效的十六進位數字 (亦即 0-9、A-F 及 a-f),這些數字將會被解譯為逸出序列的一部分。 例如, \xA1 產生 「?」,也就是字碼點 U+00A1。 不過,如果下一個字元是 「A」 或 「a」,則會改為將逸出序列解譯為 , \xA1A 並產生 「ਚ」,也就是代碼點 U+0A1A。 由此可知,將 4 個數字全數指定為十六進位數字 (例如 \x00A1),將可避免可能的錯譯。

注意

在編譯時期,逐字字串會轉換為具有所有相同逸出序列的一般字串。 因此,如果您在偵錯工具監看式視窗中檢視逐字字串,您會看到由編譯器新增的逸出字元,而非來自於您原始程式碼的逐字版本。 例如,逐字字串 @"C:\files.txt" 會顯示在監看式視窗中顯示為 「C:\\files.txt」。

格式化字串

格式字串是字串,其內容會在執行時間動態決定。 格式字串是透過內嵌「插入的運算式」或字串內大括弧內的預留位置來建立的。 大括弧內的一切 () {...} 都會解析為值,並在執行時間以格式化字串的形式輸出。 有兩種方式可用來建立格式字串:字串插補與複合格式設定。

字串插補

C# 6.0 與更新版本中提供的插補字串可透過 $ 特殊字元識別,而且在大括弧中包括插補運算式。 如果您不熟悉字串插補,請參閱 字串插補 - C# 互動式教學課程 以取得快速概觀。

使用字串插補來改進您程式碼的可讀性與可維護性。 字串插補可達成與 String.Format 方法相同的結果,但可改進使用方便性與內嵌簡潔度。

var jh = (firstName: "Jupiter", lastName: "Hammon", born: 1711, published: 1761);
Console.WriteLine($"{jh.firstName} {jh.lastName} was an African American poet born in {jh.born}.");
Console.WriteLine($"He was first published in {jh.published} at the age of {jh.published - jh.born}.");
Console.WriteLine($"He'd be over {Math.Round((2018d - jh.born) / 100d) * 100d} years old today.");

// Output:
// Jupiter Hammon was an African American poet born in 1711.
// He was first published in 1761 at the age of 50.
// He'd be over 300 years old today.

從 C# 10 開始,當用於預留位置的所有運算式也是常數位符串時,您可以使用字串插補來初始化常數位符串。

從 C# 11 開始,您可以將 原始字串常值 與字串插補結合。 您會以三個或多個連續雙引號開始和結束格式字串。 如果您的輸出字串應該包含 { 或 字元,您可以使用額外的 $ 字元來指定開始和結束插補的字元數目 {}} 輸出中會包含任何較少 {} 字元的序列。 下列範例示範如何使用該功能來顯示點與原點之間的距離,並將該點放在大括弧內:

int X = 2;
int Y = 3;

var pointMessage = $$"""The point {{{X}}, {{Y}}} is {{Math.Sqrt(X * X + Y * Y)}} from the origin.""";

Console.WriteLine(pointMessage);
// Output:
// The point {2, 3} is 3.605551275463989 from the origin.

複合格式

String.Format 利用大括弧內的預留位置來建立格式字串。 此範例可產生與上面使用之字串插補方法類似的輸出。

var pw = (firstName: "Phillis", lastName: "Wheatley", born: 1753, published: 1773);
Console.WriteLine("{0} {1} was an African American poet born in {2}.", pw.firstName, pw.lastName, pw.born);
Console.WriteLine("She was first published in {0} at the age of {1}.", pw.published, pw.published - pw.born);
Console.WriteLine("She'd be over {0} years old today.", Math.Round((2018d - pw.born) / 100d) * 100d);

// Output:
// Phillis Wheatley was an African American poet born in 1753.
// She was first published in 1773 at the age of 20.
// She'd be over 300 years old today.

如需格式化 .NET 類型的詳細資訊,請參閱 格式化 .NET 中的類型

子字串

子字串是包含在字串中的任何字元序列。 使用 Substring 方法,來從原始字串的一部分建立新的字串。 您可以使用 IndexOf 方法,來搜尋子字串的一或多個出現位置。 使用 Replace 方法,以新字串取代所有指定的子字串。 Substring如同 方法, Replace 實際上會傳回新的字串,而且不會修改原始字串。 如需詳細資訊,請參閱 如何搜尋字串如何修改字串內容

string s3 = "Visual C# Express";
System.Console.WriteLine(s3.Substring(7, 2));
// Output: "C#"

System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"

// Index values are zero-based
int index = s3.IndexOf("C");
// index = 7

存取個別字元

您可以搭配索引值使用陣列標記法來取得個別字元的唯讀存取權,如下列範例所示:

string s5 = "Printing backwards";

for (int i = 0; i < s5.Length; i++)
{
    System.Console.Write(s5[s5.Length - i - 1]);
}
// Output: "sdrawkcab gnitnirP"

String如果方法未提供您必須修改字串中個別字元的功能,您可以使用 StringBuilder 物件來修改個別字元「就地」,然後使用 方法建立新的字串來儲存結果 StringBuilder 。 在下列範例中,假設您必須以特定方式修改原始字串,並儲存結果以供日後使用︰

string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?";
System.Text.StringBuilder sb = new System.Text.StringBuilder(question);

for (int j = 0; j < sb.Length; j++)
{
    if (System.Char.IsLower(sb[j]) == true)
        sb[j] = System.Char.ToUpper(sb[j]);
    else if (System.Char.IsUpper(sb[j]) == true)
        sb[j] = System.Char.ToLower(sb[j]);
}
// Store the new string.
string corrected = sb.ToString();
System.Console.WriteLine(corrected);
// Output: How does Microsoft Word deal with the Caps Lock key?

Null 字串和空字串

空字串是 System.String 物件的執行個體,其中包含零個字元。 空字串經常用於各種程式設計案例中,來表示空白的文字欄位。 您可以在空字串上呼叫方法,因為它們是有效的 System.String 物件。 空字串會以下列方式初始化︰

string s = String.Empty;

相反地,Null 字串不會參考 物件的實例 System.String ,而且任何嘗試在 Null 字串上呼叫方法都會造成 NullReferenceException 。 不過,您可以在搭配其他字串的串連和比較作業中使用 Null 字串。 下列範例說明某些情況下,Null 字串的參考會執行,而且不會擲回例外狀況:

string str = "hello";
string nullStr = null;
string emptyStr = String.Empty;

string tempStr = str + nullStr;
// Output of the following line: hello
Console.WriteLine(tempStr);

bool b = (emptyStr == nullStr);
// Output of the following line: False
Console.WriteLine(b);

// The following line creates a new empty string.
string newStr = emptyStr + nullStr;

// Null strings and empty strings behave differently. The following
// two lines display 0.
Console.WriteLine(emptyStr.Length);
Console.WriteLine(newStr.Length);
// The following line raises a NullReferenceException.
//Console.WriteLine(nullStr.Length);

// The null character can be displayed and counted, like other chars.
string s1 = "\x0" + "abc";
string s2 = "abc" + "\x0";
// Output of the following line: * abc*
Console.WriteLine("*" + s1 + "*");
// Output of the following line: *abc *
Console.WriteLine("*" + s2 + "*");
// Output of the following line: 4
Console.WriteLine(s2.Length);

使用 stringBuilder 快速建立字串

.NET 中的字串作業已高度優化,而且在大部分情況下不會大幅影響效能。 不過,在部分案例中 (例如執行數百或數千次的緊密迴圈),字串作業可能會影響效能。 StringBuilder 類別會建立一個字串緩衝區,能在您的程式執行許多字串操作的情況下提供較佳的效能。 字串 StringBuilder 也可讓您重新指派個別字元,內建字串資料類型不支援的字元。 例如,下列程式碼能在不建立新字串的情況下變更字串內容:

System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet");
sb[0] = 'C';
System.Console.WriteLine(sb.ToString());
//Outputs Cat: the ideal pet

在下列範例中,將使用 StringBuilder 物件從一組數字類型建立字串:

var sb = new StringBuilder();

// Create a string composed of numbers 0 - 9
for (int i = 0; i < 10; i++)
{
    sb.Append(i.ToString());
}
Console.WriteLine(sb);  // displays 0123456789

// Copy one character of the string (not possible with a System.String)
sb[0] = sb[9];

Console.WriteLine(sb);  // displays 9123456789

字串、擴充方法和 LINQ

因為 String 類型會實作 IEnumerable<T>,所以您可以對字串使用 Enumerable 類別中所定義的擴充方法。 為了避免視覺雜亂,這些方法會從 IntelliSense String 中排除類型,但仍可供使用。 您也可以在字串上使用 LINQ 查詢運算式。 如需詳細資訊,請參閱 LINQ 和字串