C# string 字符串方法大全:判空、查找、截取、分割、替换一篇讲透
导语:
string就像一块刻好的石碑,字一旦刻上去就改不了,所有看起来"在修改"的方法(Replace、Trim、ToUpper......)其实都是重新刻一块新石头给你,原字符串纹丝不动。它的方法虽多,但按"判断、查找、截取分割、修改、拼接、比较"六大类来记就不会乱。本文把日常开发 90% 会用到的字符串方法全部配上可运行示例,适合入门学习和写代码时当速查表。
一、先记住 string 的三个基本事实
事实 1:string 是不可变的
字符串一旦创建,内容就不能再改。下面这段代码可以证明:
csharp
string s = "ABC";
string s2 = s;
s += "DEF"; // 不是在原字符串上写字,而是造了一个新字符串
Console.WriteLine(s); // ABCDEF
Console.WriteLine(s2); // ABC(s2 指向的旧字符串完全没变)
所以后面所有"修改类"方法(Replace、Trim、ToUpper、Substring 等)都有一个共同规律:不修改原字符串,而是返回一个新字符串,必须用变量接住返回值才有效。
事实 2:string 是引用类型,但 == 比较的是内容
csharp
string a = "hello";
string b = "hel" + "lo"; // 运行时拼接,和 a 不是同一个对象
Console.WriteLine(a == b); // True:string 重载了 ==,比较的是文本内容
Console.WriteLine(ReferenceEquals(a, b)); // False:两个对象在内存里不是同一个
所以字符串比较内容时,直接用 == 就行,这和普通引用类型用 == 比较地址不一样,C# 已经帮你处理好了。
事实 3:Length 和索引器是最基础的两个成员
csharp
string s = "abcdef";
Console.WriteLine(s.Length); // 6:字符个数
Console.WriteLine(s[0]); // a:像数组一样按下标读字符,下标从 0 开始
// s[0] = 'x'; // 报错:字符串不可变,索引器只能读不能写
【插图 1:string 方法分类地图】
二、判空与判断类方法
2.1 三种"空"先分清:null、""、空白
这是初学者最容易混的三个状态:
null:变量根本没有指向任何字符串(连石头都没有)。""(或string.Empty):字符串存在,但里面一个字符都没有(有石头,没刻字)。" ":字符串里装的是空格、制表符这类空白字符(刻了字,只是看不见)。
2.2 IsNullOrEmpty 和 IsNullOrWhiteSpace
csharp
string s1 = null;
string s2 = "";
string s3 = " ";
string s4 = "abc";
// IsNullOrEmpty:null 或空字符串都算 true
Console.WriteLine(string.IsNullOrEmpty(s1)); // True
Console.WriteLine(string.IsNullOrEmpty(s2)); // True
Console.WriteLine(string.IsNullOrEmpty(s3)); // False(空白不是空字符串)
Console.WriteLine(string.IsNullOrEmpty(s4)); // False
// IsNullOrWhiteSpace:null、空、只含空白都算 true
Console.WriteLine(string.IsNullOrWhiteSpace(s3)); // True
Console.WriteLine(string.IsNullOrWhiteSpace(s4)); // False
【插图 2:null、空字符串、空白的判断结果对比】
实战中判断一个字符串"能不能用",优先用 IsNullOrWhiteSpace,因为用户输入几个空格通常也应当作没填。
2.3 Contains、StartsWith、EndsWith
csharp
string s = "Hello World";
// Contains:是否包含某个子串
Console.WriteLine(s.Contains("World")); // True
Console.WriteLine(s.Contains("world")); // False(默认区分大小写)
// StartsWith / EndsWith:以什么开头 / 结尾
Console.WriteLine(s.StartsWith("Hello")); // True
Console.WriteLine(s.EndsWith("World")); // True
版本说明:在 .NET Core 2.1 / .NET 5 及更高版本中,这三个方法还支持直接传
char字符,比如s.Contains('W');老版本只支持字符串参数。
2.4 忽略大小写的判断
判断时不区分大小写,不要先 ToLower() 再比,推荐直接给比较方式:
csharp
string s = "Hello World";
bool ok = s.StartsWith("hello", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(ok); // True
StringComparison 是什么、为什么推荐 Ordinal,第七节会专门讲。
三、查找位置类方法
3.1 IndexOf 和 LastIndexOf
csharp
string s = "banana";
// IndexOf:从前往后找,返回第一次出现的下标,找不到返回 -1
Console.WriteLine(s.IndexOf('a')); // 1
Console.WriteLine(s.IndexOf("na")); // 2
Console.WriteLine(s.IndexOf('x')); // -1
// LastIndexOf:从后往前找,返回最后一次出现的下标
Console.WriteLine(s.LastIndexOf('a')); // 5
3.2 IndexOfAny 和 LastIndexOfAny
IndexOfAny 一次给一组字符,谁先出现就返回谁的位置:
csharp
string s = "Hello, World!";
char[] punctuation = { ',', '!', '?' };
Console.WriteLine(s.IndexOfAny(punctuation)); // 5(逗号先出现)
Console.WriteLine(s.LastIndexOfAny(punctuation)); // 12(感叹号在最后)
3.3 方法归纳
| 方法 | 作用 | 找不到时 |
|---|---|---|
IndexOf(char/string) |
从前往后找第一次出现的位置 | 返回 -1 |
LastIndexOf(...) |
从后往前找最后一次出现的位置 | 返回 -1 |
IndexOfAny(char[]) |
一组字符中谁先出现 | 返回 -1 |
LastIndexOfAny(char[]) |
一组字符中谁最后出现 | 返回 -1 |
四、截取与分割类方法
4.1 Substring:从指定位置截取
csharp
string s = "Hello World";
// 一个参数:从下标 6 一直截到末尾
Console.WriteLine(s.Substring(6)); // World
// 两个参数:从下标 0 开始,截取 5 个字符
Console.WriteLine(s.Substring(0, 5)); // Hello
4.2 范围运算符:更简洁的截取方式(C# 8.0 及以上)
C# 8 引入了范围运算符 ..,截取字符串时比 Substring 更直观:
csharp
string s = "Hello World";
Console.WriteLine(s[..5]); // Hello:从头截到下标 5 之前
Console.WriteLine(s[6..]); // World:从下标 6 截到末尾
Console.WriteLine(s[2..5]); // llo:从下标 2 截到下标 5 之前
4.3 Split:按分隔符切成数组
csharp
string csv = "a,b,,c,";
// 直接切:连续分隔符和末尾分隔符会产生空字符串条目
string[] r1 = csv.Split(',');
// 结果:["a", "b", "", "c", ""]
// RemoveEmptyEntries:自动去掉空条目
string[] r2 = csv.Split(',', StringSplitOptions.RemoveEmptyEntries);
// 结果:["a", "b", "c"]
StringSplitOptions 常用取值:
| 取值 | 作用 |
|---|---|
None |
默认,保留空条目 |
RemoveEmptyEntries |
去掉空字符串条目 |
TrimEntries |
自动去掉每个条目两端的空白(.NET 5 及以上) |
| `RemoveEmptyEntries | TrimEntries` |
还可以一次传多个分隔符:
csharp
string s = "a-b,c";
string[] parts = s.Split(new[] { '-', ',' });
// 结果:["a", "b", "c"]
五、修改与转换类方法
【插图 3:修改类方法返回新字符串,原字符串不变】
5.1 Replace:替换
csharp
string s = "a-b-c";
string result = s.Replace("-", "_"); // 所有 "-" 都替换,不只是第一个
Console.WriteLine(result); // a_b_c
Console.WriteLine(s); // a-b-c(原字符串没变)
5.2 Trim、Pad:去空白和补位
csharp
// Trim:去掉两端空白(也可以指定要去掉的字符)
Console.WriteLine(" hi ".Trim()); // hi
Console.WriteLine("**hi**".Trim('*')); // hi
Console.WriteLine(" hi ".TrimStart()); // hi(只去开头)
Console.WriteLine(" hi ".TrimEnd()); // hi(只去结尾)
// PadLeft / PadRight:长度不够时用指定字符补齐
Console.WriteLine("42".PadLeft(5, '0')); // 00042(补零场景很常用)
Console.WriteLine("42".PadRight(5, '*')); // 42***
5.3 大小写转换
csharp
Console.WriteLine("hello".ToUpper()); // HELLO
Console.WriteLine("HELLO".ToLower()); // hello
5.4 Insert 和 Remove:插入与删除
csharp
// Insert:在下标位置插入内容
Console.WriteLine("Hello World".Insert(5, ",")); // Hello, World
// Remove:从下标位置开始删除若干字符
Console.WriteLine("Hello, World".Remove(5, 2)); // HelloWorld
5.5 ToCharArray:转成字符数组
需要逐个字符处理时,先转成数组:
csharp
char[] chars = "abc".ToCharArray();
foreach (char c in chars)
{
Console.WriteLine(c);
}
5.6 方法归纳表
| 方法 | 作用 | 关键点 |
|---|---|---|
Replace(old, new) |
替换全部匹配项 | 有 char 和 string 两个重载 |
Trim() / TrimStart() / TrimEnd() |
去两端空白或指定字符 | 不去中间的空白 |
PadLeft() / PadRight() |
补位到指定长度 | 常用于补零、对齐 |
ToUpper() / ToLower() |
转大写 / 小写 | 同样返回新字符串 |
Insert(index, value) |
指定位置插入 | 下标从 0 开始 |
Remove(start, length) |
指定位置删除 | 按位置删,不是按内容 |
ToCharArray() |
转字符数组 | 可配合排序、反转等算法 |
六、拼接、格式化与新语法
6.1 Concat 和 Join(静态方法)
csharp
// Concat:首尾相接,不加分隔符
Console.WriteLine(string.Concat("a", "b", "c")); // abc
// Join:用分隔符把多个值连起来
Console.WriteLine(string.Join(",", 1, 2, 3)); // 1,2,3
Console.WriteLine(string.Join("、", new[] { "看书", "跑步" })); // 看书、跑步
6.2 Format 和字符串插值
csharp
// Format:用占位符 {0}、{1} 拼接
string s1 = string.Format("姓名:{0},年龄:{1}", "张三", 18);
// 字符串插值:直接在 { } 里写变量,更直观,日常首选
string name = "张三";
int age = 18;
string s2 = $"姓名:{name},年龄:{age}";
6.3 原始字符串(C# 11.0 及以上)
路径、JSON、SQL 里有大量引号和反斜杠时,用三个双引号包裹的原始字符串,既不用转义,还能保留缩进:
csharp
string json = """
{
"name": "张三",
"age": 18
}
""";
循环里大量拼接、动态拼装 SQL/HTML 等场景,应该用
StringBuilder,可以参考我之前的《C# StringBuilder 详解》;按规则匹配、提取文本的场景(邮箱、手机号、提取数字)则用正则,可以参考《C# 正则表达式零基础入门》。
七、比较类方法与 StringComparison
7.1 Equals 和 Compare
csharp
// Equals:判断两个字符串是否相等
Console.WriteLine(string.Equals("a", "a")); // True
Console.WriteLine("a".Equals("A", StringComparison.OrdinalIgnoreCase)); // True
// Compare:比较大小,小于返回 -1,等于返回 0,大于返回 1,常用于排序
Console.WriteLine(string.Compare("a", "b")); // -1
Console.WriteLine(string.Compare("b", "a")); // 1
Console.WriteLine(string.Compare("a", "a")); // 0
// CompareOrdinal:纯粹按字符编码数值比较,不考虑语言文化
Console.WriteLine(string.CompareOrdinal("a", "b")); // -1
7.2 StringComparison 枚举速查
| 取值 | 含义 | 典型用途 |
|---|---|---|
Ordinal |
按字符原始数值逐字节比较 | 路径、协议、关键字等精确匹配 |
OrdinalIgnoreCase |
同上,忽略大小写 | 不区分大小写的关键字判断(推荐) |
CurrentCulture |
按当前系统语言文化比较(默认行为) | 界面显示、文本排序 |
CurrentCultureIgnoreCase |
同上,忽略大小写 | 不区分大小写的用户输入 |
InvariantCulture |
与具体语言文化无关的固定格式 | 持久化数据格式(较少用) |
八、常见坑
-
把修改类方法当成"原地修改",没有接住返回值。
s.Replace("-", "_")执行完s还是原样,新字符串在返回值里被丢掉了。正确写法是s = s.Replace("-", "_");所有 Replace、Trim、ToUpper 类方法都一样。 -
判空时直接用
s == ""或s.Length == 0,遇到 null 直接抛 NullReferenceException。 先判空再操作,统一用string.IsNullOrEmpty(s);用户输入类场景用IsNullOrWhiteSpace把纯空格也拦下来。 -
IndexOf、Contains 等默认按当前语言文化比较,做精确匹配时可能得到意外结果。 比如判断文件路径、协议字段、关键字,应显式指定
StringComparison.Ordinal(需要忽略大小写就用OrdinalIgnoreCase),让比较纯粹按字符数值进行,不受系统语言影响。 -
Split 后不处理空条目,拿到一堆空字符串。 连续分隔符、末尾分隔符都会切出
"",绝大多数业务场景都应加上StringSplitOptions.RemoveEmptyEntries,需要顺手去空格再加TrimEntries。 -
Substring 起始位置或长度算错,越界抛 ArgumentOutOfRangeException。 截取前先确认下标不超过
Length;C# 8 及以上优先用范围运算符s[..5]、s[6..],写法更短也不容易数错长度。
九、小结
- string 是不可变的引用类型 ,所有修改类方法都返回新字符串、原串不动;
==已被重载为内容比较,判空用IsNullOrEmpty/IsNullOrWhiteSpace。 - 方法按六类记:判断 (Contains、StartsWith、EndsWith)、查找 (IndexOf 系列)、截取分割 (Substring、Split、范围运算符)、修改 (Replace、Trim、Pad、ToUpper、Insert、Remove)、拼接 (Concat、Join、Format、
$"")、比较(Equals、Compare、Ordinal)。 - 精确匹配(路径、协议、关键字)显式用
StringComparison.Ordinal,忽略大小写用OrdinalIgnoreCase。 - 新语法按需用:C# 8 范围运算符、C# 11 原始字符串;循环大量拼接找 StringBuilder,规则匹配找正则。


