Tip
本文是已了解至少一种编程语言并正在学习 C# 的开发人员的 “基础知识 ”部分的一部分。 如果你不熟悉编程,请先 学习入门 教程。
来自 Java 或 C++? C# string 是一种不可变的引用类型,由 String 提供支持。 UTF-16 是内存中编码,类似于Java的 String。 与 C/C++ 不同,字符串不会以 null 结尾,不会衰减到指针中。
字符串是字符序列。 在 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 关键字。 它与其他内置类型关键字(int、bool、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路径或正则表达式模式)时,请使用 verbatim 文本。
- 将 原始字符串文本 用于多行文本或结构化格式文本,例如内联 JSON、SQL、XML 或格式化消息块。
- 当需要嵌入值时,给上述任意字面量添加
$前缀即可得到插值字符串。 - 向字面量添加后缀
u8,以生成 UTF-8 字节序列(即byte的 ReadOnlySpan<T>),供面向字节的 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 (制表符)、 \" (文本引号)、 \\ (文本反斜杠)、 \0 (null char)和 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、正则表达式模式以及格式化文本块,在这些场景中,源文本应看起来与输出结果一致:
// 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# 中日常字符串操作的快速指南:
| 类别 | 它涵盖的内容 |
|---|---|
| 搜寻 | 查找字符或子字符串、测试前缀和后缀 |
| 拆分 | 将字符串分解为分隔符上的子字符串 |
| 连接 | 合并字符串 — +、插值、Concat、Join |
| 修改 | 生成转换后的副本 — Replace、Trim、Substring |
| 比较 | 使用合适的 StringComparison 测试相等性和排序 |
String 参考文档中详细说明了完整的 API 全貌——每个重载、每个方法。