最安全的Java字符串截取方式是先校验索引合法性并处理Unicode补充字符,推荐用BreakIterator或Apache Commons Lang的StringUtils.substring()。

Java 字符串截取最常用、最安全的方式是 substring(),但直接用它可能抛 StringIndexOutOfBoundsException,尤其在边界不明确时;若涉及 Unicode 补充字符(如 emoji 或某些生僻汉字),substring() 还会切坏字符——这不是 bug,是它按 UTF-16 代码单元计数的固有行为。
用 substring() 截取子串:索引必须合法且理解“左闭右开”
substring(int beginIndex) 和 substring(int beginIndex, int endIndex) 是最常用的重载。关键点不是“怎么写”,而是“怎么防错”:
-
beginIndex必须 ≥ 0 且 ≤ 字符串长度,endIndex必须 ≥beginIndex且 ≤ 字符串长度;越界立刻抛异常 - 它是“左闭右开”:例如
"abc".substring(1, 2)返回"b",不是"bc" - 如果不确定长度,先做校验:
if (s != null && index >= 0 && index - 不要用
substring(0, -1)试图“去掉末尾字符”——负索引非法;应改用s.substring(0, Math.max(0, s.length() - 1))
处理 emoji 或中文生僻字时,别用 substring() 直接按位置切
Java 的 String 内部用 UTF-16 编码,一个 emoji(如 "??")或某些汉字(如 U+20000 起的扩展 B 区汉字)占两个 char(即一个代理对)。substring(0, 1) 可能只取到高代理位,导致乱码或 IllegalArgumentException(在转成 String 后调用 codePointAt() 等方法时暴露)。
- 正确做法是按 Unicode 码点(code point)操作:
s.codePoints().limit(n).collect(StringBuilder::new, (sb, cp) -> sb.appendCodePoint(cp), StringBuilder::append).toString() - 更实用的封装:用
java.text.BreakIterator按字符边界切分,或借助 Apache Commons Lang 的StringUtils.substring()(它内部已处理代理对) - 简单判断是否安全:若字符串只含 BMP 区字符(即
s.codePoints().allMatch(cp -> cp ),substring()可放心用
替代方案:正则 replaceFirst() 或 replaceAll() 实现“按内容截取”
当需求不是“从第 n 位开始取 m 位”,而是“取冒号前的部分”或“去掉最后一个斜杠后的路径”,硬算索引容易出错,正则更直观可靠:
立即学习“Java免费学习笔记(深入)”;
-
s.replaceFirst(":(.*)", "")→ 取第一个冒号前的内容(注意:要转义) -
s.replaceAll("^(.*?)/[^/]*$", "$1")→ 去掉路径末尾文件名,留父目录(非贪婪匹配防误伤) - 性能提示:正则比
substring()慢,但可读性和健壮性高;高频场景可预编译Pattern复用 - 注意
replaceAll()第二个参数是替换字符串,不是正则;要引用捕获组用$1,不是\1
真正麻烦的从来不是“怎么截”,而是“截哪儿”——边界条件(空字符串、null、emoji、多字节标点)和语义(按字节?字符?码点?词?)混在一起时,substring() 就只是个基础工具,得配合校验、编码意识和业务逻辑一起用。










