调用 Substring 前必须检查字符串长度,否则易因索引越界抛 ArgumentOutOfRangeException;推荐用 SafeRight 扩展方法安全截取末尾 N 位。

用 Substring 截取字符串前要先检查长度
直接调用 Substring 而不验证索引位置,是 ArgumentOutOfRangeException 最常见来源。比如想取后 3 个字符:str.Substring(str.Length - 3),但当 str 长度小于 3 时就崩了。
实操建议:
- 始终用
Math.Max(0, Math.Min(startIndex, str.Length))做边界兜底,或更稳妥地先判断:if (str.Length >= 3) { str.Substring(str.Length - 3); } - 想安全取“末尾 N 位”,推荐封装成扩展方法:
public static string SafeRight(this string s, int length) => s?.Length -
Substring不支持负数索引(不像 Python),传-1会直接抛异常,不是从末尾倒数
按分隔符截取用 Split 还是 IndexOf + Substring?
取决于你是否需要全部分割结果。Split 简单但有隐性开销:它会分配数组、处理空项、默认跳过空字符串——如果你只想要第一段或某一段,这纯属浪费。
实操建议:
- 只要取第一个
"|"前的内容:用str.IndexOf('|')判断位置,再Substring(0, index),比str.Split('|')[0]快且不触发 GC - 要保留空段(如
"a||c"想得三个元素),必须显式传StringSplitOptions.None,否则默认丢掉空字符串 -
Split的重载里,count参数能限制最多切几刀,比如str.Split(new[] {' '}, 3)只切前两个空格,剩下整块当第三项,适合解析固定格式日志
处理中文或 Emoji 时别被 Length 骗了
string.Length 返回的是 UTF-16 code unit 数,不是字符数。一个 ?(U+1F40D)占两个 char,所以 "?".Length == 2;某些中文字在生僻字扩展区也如此。用 Substring 按索引截取可能切在代理对中间,导致乱码或异常。
实操建议:
- 真要按“人眼字符”数截取,用
System.Globalization.StringInfo:var info = new StringInfo(str); info.SubstringByTextElements(0, 5) - 日常开发中,如果确定文本只含 ASCII 或常用汉字(基本多在 BMP 区),
Length和Substring是安全的,不用过度防范 - 正则匹配
\w+或用Span<char>.Slice()</char>都绕不开 UTF-16 边界问题,本质和Substring同源,别以为换 API 就自动“按字符”了
Span<char></char> 截取能避免内存分配,但有使用前提
在高频循环或性能敏感路径(如协议解析),用 Span<char></char> 替代 string.Substring 可省掉每次新建字符串的堆分配。但它只是原字符串的“视图”,不能脱离原字符串生命周期存在。
实操建议:
- 从字符串创建:
Span<char> span = str.AsSpan().Slice(start, length)</char>,之后可传给接受ReadOnlySpan<char></char>的方法(如int.TryParse) - 不能把
Span存到字段或返回给调用方——它可能指向栈内存,函数返回后就失效;若需持久化,必须调用ToString()或拷贝到string - 注意兼容性:
Span在 .NET Core 2.1+ 和 .NET 5+ 才完全可用,.NET Framework 不支持
有些边界条件不会报错但结果不对,比如用 Substring 截取超长索引时抛异常,而用 Span.Slice 会直接崩溃——这两者的失败表现不同,调试时得看准调用栈源头。


















