字串和字串常值

字串是類型 String 的物件,其值為文字。 在內部,文字被儲存為循序只讀的Char物件集合。 Length字串的 屬性代表它所包含的物件數目Char,而不是 Unicode 字元的數目。 若要存取字串中的個別 Unicode 字碼點,請使用 StringInfo 物件。

string 與 System.String

在 C# 中 string ,關鍵詞是 的 String別名, String 因此 和 string 是相等的。 不使用string時,請仍然使用提供的別名using System;。 類別 String 提供許多方法來安全地建立、作和比較字串。 此外,C# 語言會多載一些運算符,以簡化常見的字串作業。 如需 關鍵詞的詳細資訊,請參閱 string。 如需型別及其方法的詳細資訊,請參閱 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);

除了使用 chars 陣列初始化字串之外,您不會使用 new 運算符來建立字串物件。

初始化字串,並將 Empty 常數值用於建立字串長度為零的新 String 物件。 零長度字串的字串常值表示為 ""。 藉由使用 Empty 值來初始化字串,而不是 null,您可以降低發生 NullReferenceException 的機率。 在嘗試存取字串之前,請使用靜態 IsNullOrEmpty(String) 方法來驗證字串串的值。

字串的不變性

字串物件是不 可變的:建立字串對象之後就無法變更。 String所有看似修改字串的方法和 C# 運算符,實際上都會傳回新字串物件中的結果。 在下列範例中,當 s1 和 s2 的內容串連成單一字串時,兩個原始字串未被修改。 運算子 += 會建立包含合併內容的新字串。 該新物件被指派給變數 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 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."

原始字串常值

你可以用 原始字串文字 以更容易地建立多行字串,或者使用任何需要跳脫序列的字元。 原始字串常值 會去除使用跳脫字元的需求。 您可以撰寫字串,包括空格符格式設定,您希望它在輸出中顯示的方式。 原始字串常值:

  • 以至少三個雙引號字元的"""序列開頭和結尾。 您可以使用三個以上的連續字元來開始和結束序列,以支援包含三個或多個重複引號字元的字串常值。
  • 單行原始字串文字需要在同一行中有開頭和結尾的引號字元。
  • 多行原始字串常值需要在它們各自的行上放置開頭和結尾的引號符號。
  • 在多行原始字串文字中,結束引號左邊的任何空白都會從原始字串文字的所有行中移除。
  • 在多行原始字串常值中,會忽略位於同一行開頭引號後面的空白字符。
  • 在多行原始字串常值中,開頭引號後的僅包含空格的行也會被納入字串常值中。

下列範例示範下列規則:

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"
  ]
}
""";

小提示

Visual Studio 和 C# 開發套件會在原始字串常值包含 JSON 資料或規則運算式時,提供一些驗證和語法醒目提示。

這些工具會解析文字。 如果工具確信文字代表 JSON 或規則運算式,編輯器會提供語法著色。

您可以在聲明上方新增註解,以指示格式,以改善該體驗:

  • // lang=json 表示原始字串文字常數代表 JSON 的資料。
  • // lang=regex 表示原始字串常值代表正則表示式。

當原始字串常值用作引數,且參數使用 System.Diagnostics.CodeAnalysis.StringSyntaxAttribute 來指示格式時,這些工具會針對某些格式類型驗證原始字串常值。 支援 JSON 和正規表示式。

對於某些格式,註釋或屬性會啟用程式碼提示,根據格式提供對字串常量的修正。

字串逸出序列

逸出序列 角色名稱 Unicode 編碼
\' 單引號 0x0027
\" 雙引號 0x0022
\\ 反斜線 0x005C
\0 零 0x0000
\a 警報 0x0007
\b 退格鍵 0x0008
\e 逃脫 0x001B
\f 表單摘要 0x000C
\n 換行 0x000A
\r 回車鍵 0x000D
\t 水平 Tab 鍵 0x0009
\v 垂直索引標籤 0x000B
\u Unicode 逸出序列 (UTF-16) \uHHHH(範圍:0000 - FFFF;例如:\u00E7 = “ç”)
\U Unicode 逸出序列 (UTF-32) \U00HHHHHH(範圍:000000 - 10FFFF;例如:\U0001F47D = “👽”)
\x Unicode 逸出序列與 “\u” 類似,但具有可變長度。 \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# 教學 課程以取得數個範例。

使用字串插補來改善程式碼的可讀性和可維護性。 字串插補達到與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.

當所有用於佔位符的表達式也是常數字串時,您可以使用字串插值來初始化常數字串。

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

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.

逐字字串插補

C# 也允許逐字字面值字串插值,例如使用 $@ 或 @$ 語法來跨越多行。

若要以字面方式解讀跳脫序列,請使用逐字串字面常值。 插入的逐字字串以 $ 字元開頭,接著是 @ 字元。 您可以依任何順序使用 $ 和 @ 標記: $@"..." 和 @$"..." 都是有效的插補逐字字串。

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

// Output:
// Jupiter Hammon
//     was an African American poet born in 1711.
// He was first published in 1761
// at the age of 50.

複合格式

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 中的複合格式設定。

子字串

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

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"

若要進行更廣泛的字串操作,您可以使用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 字串的參考會觸發例外狀況,以及不會觸發例外狀況的情形:

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 類別中定義的擴充方法。 為了避免視覺雜亂,這些方法會從 String 類型的 IntelliSense 中排除,但仍然可以使用。 您也可以在字串上使用LINQ查詢表示式。 如需詳細資訊,請參閱 LINQ 和字串。