应先用 indexof 查找分隔符位置,再用 substring(0, pos) 截取前段,且必须判断 pos 是否为 -1 以避免异常;substring 第二个参数是长度而非结束索引,这是高频错误点。

用 Substring + IndexOf 截取指定字符前的内容
要拿到某个分隔符(比如 "_")前面的部分,核心是先定位位置,再切片。直接调 Substring(0, index) 即可,但必须处理找不到字符的情况,否则 IndexOf 返回 -1 会导致 ArgumentOutOfRangeException。
常见错误现象:"abc_def".Substring(0, "abc_def".IndexOf('_')) 在字符串不含 '_' 时崩溃;或者误用 Length 当结束索引传给 Substring。
- 先用
IndexOf查找,结果存为变量,避免重复计算 - 检查返回值是否为
-1,决定返回原串还是空串(按业务逻辑选) - 注意
Substring第二个参数是长度,不是结束索引 —— 这是高频出错点
string s = "name_value";
int pos = s.IndexOf('_');
string before = pos == -1 ? s : s.Substring(0, pos); // → "name"
用 Substring + LastIndexOf 获取最后一个分隔符后的内容
如果目标是取最后出现的 "_" 后面那段(比如文件名中的扩展名前缀),LastIndexOf 比 IndexOf 更稳妥。它能避免路径中多个下划线时取错位置,比如 "a_b_c.txt" 中想取 "c.txt" 而不是 "b_c.txt"。
容易踩的坑:直接写 s.Substring(pos + 1) 忽略了 pos 为 -1 的边界,或没考虑 pos + 1 超出字符串长度(如 "abc_" 结尾有下划线)。
-
LastIndexOf更适合“取最后一段”的场景,尤其是路径、带版本号的标识符 - 安全写法是判断
pos >= 0 && pos 再截取 - 若只要最后一个
"."后的内容(模拟扩展名),优先用Path.GetExtension,更健壮
string s = "user_profile_v2_data";
int pos = s.LastIndexOf('_');
string after = pos == -1 ? string.Empty : s.Substring(pos + 1); // → "data"
用 Split 简单分段,但要注意空项和性能
对简单结构(如 "key_value")用 Split('_') 最直观,但默认会拆出所有片段,且遇到连续分隔符("a__b")或首尾分隔符("_a_b_")时生成空字符串,容易引发 IndexOutOfRangeException。
使用场景:确定只有 1 个分隔符、或只关心前/后第一段,且不介意少量内存分配。不适合高频调用或超长字符串。
- 加
StringSplitOptions.RemoveEmptyEntries过滤空项,避免越界访问 - 限制分割次数,如
Split('_', 2)只切一次,返回最多两个元素,效率更高 -
Split总是分配新数组,短字符串小开销,循环里频繁调用建议改用IndexOf方案
string[] parts = "config_name_v1".Split('_', 2); // → ["config", "name_v1"]
string afterFirst = parts.Length > 1 ? parts[1] : string.Empty;
正则 Regex.Match 处理复杂模式,别为简单需求引入它
真需要“取第 n 个下划线后、且后面跟数字的内容”,才值得上正则。日常截取前后段用 Regex 是杀鸡用牛刀:编译慢、易写错、难调试、GC 压力大。
典型错误:写 @"_(\w+)" 却忘了 Match.Success 判断,或用 Groups[1].Value 访问未捕获组导致异常。
- 只在模式动态、含条件(如“下划线后必须是字母开头”)、或需多规则复用时考虑正则
- 静态分隔符优先用
IndexOf/Split,它们更快更直观 - 若已引用
System.Text.RegularExpressions且逻辑复杂,再评估正则可维护性
// 不推荐用于简单截取
var m = Regex.Match("id_123_name", @"_(\d+)_");
string num = m.Success ? m.Groups[1].Value : string.Empty;
最常被忽略的是 IndexOf 和 Substring 的组合里,长度参数到底是“长度”还是“索引”——这个混淆在调试时往往卡几分钟。另外,所有方案都要显式处理分隔符不存在的情况,C# 不会自动 fallback。











