1. 简介
在本篇教程中,我们将介绍几种从 String 中去除首尾指定字符的方法。为了便于说明,所有示例都以去除字符串中的前导和尾随零(zero)为例。
每种实现我们都提供两个方法:一个用于去除前导零,另一个用于去除尾随零。
这个问题有一个边界情况需要注意:如果输入字符串只包含零,我们是返回一个空字符串,还是保留一个 "0"
?针对这两种需求,我们会在每种实现中分别给出对应的处理方式。
本文所有实现均配有单元测试,你可以在 GitHub 上找到这些测试代码。
2. 使用 StringBuilder
第一种方案是使用 StringBuilder 构造原始字符串,然后从开头或结尾逐个删除不需要的字符:
String removeLeadingZeroes(String s) {
StringBuilder sb = new StringBuilder(s);
while (sb.length() > 0 && sb.charAt(0) == '0') {
sb.deleteCharAt(0);
}
return sb.toString();
}
String removeTrailingZeroes(String s) {
StringBuilder sb = new StringBuilder(s);
while (sb.length() > 0 && sb.charAt(sb.length() - 1) == '0') {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
注意,在删除尾随零时我们使用了 StringBuilder.setLength()
而不是 deleteCharAt()
,因为后者效率较低。
如果我们 不想在全为零的情况下返回空字符串,只需要 在只剩一个字符时停止循环 即可:
String removeLeadingZeroes(String s) {
StringBuilder sb = new StringBuilder(s);
while (sb.length() > 1 && sb.charAt(0) == '0') {
sb.deleteCharAt(0);
}
return sb.toString();
}
String removeTrailingZeroes(String s) {
StringBuilder sb = new StringBuilder(s);
while (sb.length() > 1 && sb.charAt(sb.length() - 1) == '0') {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
✅ 优点:逻辑清晰,性能可接受
❌ 缺点:手动遍历效率略低
3. 使用 String.substring()
第二种方法是通过查找第一个或最后一个非零字符的位置,然后调用 substring()
截取剩余部分:
String removeLeadingZeroes(String s) {
int index;
for (index = 0; index < s.length(); index++) {
if (s.charAt(index) != '0') {
break;
}
}
return s.substring(index);
}
String removeTrailingZeroes(String s) {
int index;
for (index = s.length() - 1; index >= 0; index--) {
if (s.charAt(index) != '0') {
break;
}
}
return s.substring(0, index + 1);
}
⚠️ 注意:变量 index
需要定义在循环外部,以便后续使用。
⚠️ 注意:由于 indexOf()
和 lastIndexOf()
只能精确匹配,我们只能手动查找非零字符。
如果 不想返回空字符串,只需修改循环条件即可:
String removeLeadingZeroes(String s) {
int index;
for (index = 0; index < s.length() - 1; index++) {
if (s.charAt(index) != '0') {
break;
}
}
return s.substring(index);
}
String removeTrailingZeroes(String s) {
int index;
for (index = s.length() - 1; index > 0; index--) {
if (s.charAt(index) != '0') {
break;
}
}
return s.substring(0, index + 1);
}
✅ 优点:逻辑直观,性能优于 StringBuilder
❌ 缺点:需要手动控制索引,稍显繁琐
4. 使用 Apache Commons
Apache Commons 提供了非常实用的工具类,其中 org.apache.commons.lang3.StringUtils
就包含我们需要的功能。
4.1. Maven 依赖
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
4.2. 实现方式
StringUtils
提供了 stripStart()
和 stripEnd()
方法,正好满足我们的需求:
String removeLeadingZeroes(String s) {
return StringUtils.stripStart(s, "0");
}
String removeTrailingZeroes(String s) {
return StringUtils.stripEnd(s, "0");
}
⚠️ 注意:这两个方法的第二个参数是字符集合,不是字符串匹配,比如传 "01"
会同时去除 '0'
和 '1'
。
如果 不想去除所有零,我们需要手动处理边界情况:
String removeLeadingZeroes(String s) {
String stripped = StringUtils.stripStart(s, "0");
if (stripped.isEmpty() && !s.isEmpty()) {
return "0";
}
return stripped;
}
String removeTrailingZeroes(String s) {
String stripped = StringUtils.stripEnd(s, "0");
if (stripped.isEmpty() && !s.isEmpty()) {
return "0";
}
return stripped;
}
✅ 优点:代码简洁,功能强大
❌ 缺点:需引入第三方依赖
5. 使用 Guava
Guava 也提供了类似的功能,使用 com.google.common.base.CharMatcher
可以轻松处理字符匹配。
5.1. Maven 依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
⚠️ Android 用户请使用 27.0-android
版本。
5.2. 实现方式
使用 trimLeadingFrom()
和 trimTrailingFrom()
方法:
String removeLeadingZeroes(String s) {
return CharMatcher.is('0').trimLeadingFrom(s);
}
String removeTrailingZeroes(String s) {
return CharMatcher.is('0').trimTrailingFrom(s);
}
同样,如果不想全部去除,可手动判断:
String removeLeadingZeroes(String s) {
String stripped = CharMatcher.is('0').trimLeadingFrom(s);
if (stripped.isEmpty() && !s.isEmpty()) {
return "0";
}
return stripped;
}
String removeTrailingZeroes(String s) {
String stripped = CharMatcher.is('0').trimTrailingFrom(s);
if (stripped.isEmpty() && !s.isEmpty()) {
return "0";
}
return stripped;
}
✅ 优点:API 灵活,支持复杂匹配规则
❌ 缺点:依赖较重,学习成本略高
6. 使用正则表达式
既然是模式匹配问题,正则表达式自然也是不错的选择:
String removeLeadingZeroes(String s) {
return s.replaceAll("^0+", "");
}
String removeTrailingZeroes(String s) {
return s.replaceAll("0+$", "");
}
如果不想全部去除,可以结合断言(negative lookahead)来保留一个零:
String removeLeadingZeroes(String s) {
return s.replaceAll("^0+(?!$)", "");
}
String removeTrailingZeroes(String s) {
return s.replaceAll("(?!^)0+$", "");
}
⚠️ "(?!^)"
表示不是字符串开头,"(?!$)"
表示不是结尾。
✅ 优点:语法强大,一行搞定
❌ 缺点:正则表达式可读性差,维护成本高
7. 总结
本文介绍了多种去除字符串首尾指定字符的方法,包括:
- 使用
StringBuilder
手动删除 - 使用
substring()
截取 - 使用 Apache Commons 的
StringUtils
- 使用 Guava 的
CharMatcher
- 使用正则表达式
每种方法都有其适用场景,选择哪种实现主要取决于个人偏好和项目约束。性能方面,substring()
通常优于 StringBuilder
,而工具类方法则胜在简洁可靠。
一如既往,本文所有示例代码均可在 GitHub 获取。