从字符串中删除前导和尾随字符
一、简介
在这个简短的教程中,我们将看到几种从字符串中删除前导和尾随字符的方法。为了简单起见,我们将删除示例中的零。
对于每个实现,我们将创建两种方法:一种用于前导,另一种用于尾随零。
这个问题有一个边缘情况:当输入仅包含零时我们想要做什么?返回一个空的字符串,还是一个包含单个零的字符串?我们将在每个解决方案中看到这两个用例的实现。
我们为每个实现提供了单元测试,您可以找到 在 GitHub 上。
2. 使用StringBuilder
在我们的第一个解决方案中,我们将使用原始 String 创建一个 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() 而不是 StringBuilder.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);
}
请注意,我们必须在 for 循环之前声明变量 index,因为我们想在循环范围之外使用该变量。
另请注意,我们必须手动查找非零字符,因为 String.indexOf() 和 String.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);
}
4. 使用 Apache Commons
Apache Commons 有许多有用的类,包括 org.apache.commons.lang.StringUtils。更准确地说,这个类位于 Apache Commons Lang3 中。
4.1. 依赖关系
我们可以通过将此依赖项插入到我们的 pom.xml 文件中来使用 Apache Commons Lang3 :
<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");
}
不幸的是,我们无法配置是否要删除所有出现的情况。因此,我们需要手动控制它。
如果输入不为空,但剥离的 String 为空,那么我们必须返回一个零:
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;
}
请注意,这些方法接受 String 作为其第二个参数。这个字符串代表一组字符,而不是我们要删除的序列。
例如,如果我们传递“01”,它们将删除任何前导或尾随字符,即'0'或'1' 。
5. 使用Guava
Guava 还提供了许多实用程序类。对于这个问题,我们可以使用com.google.common.base.CharMatcher,它提供了与匹配字符交互的实用方法。
5.1.依赖关系
要使用 Guava,我们应该将以下依赖项添加到我们的 pom.xml文件:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
请注意,如果我们想在 Android 应用程序中使用 Guava,则应该使用版本 27.0-android。
5.2. 实现
在我们的例子中,我们对 trimLeadingFrom() 和 trimTrailingFrom() 感兴趣。
顾名思义,它们分别从与 CharMatcher 匹配的 String 中删除任何前导或尾随字符:
String removeLeadingZeroes(String s) {
return CharMatcher.is('0').trimLeadingFrom(s);
}
String removeTrailingZeroes(String s) {
return CharMatcher.is('0').trimTrailingFrom(s);
}
它们与我们看到的 Apache Commons 方法具有相同的特征。
因此,如果我们不想删除所有零,我们可以使用相同的技巧:
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;
}
请注意,使用 CharMatcher 我们可以创建更复杂的匹配规则。
6. 使用正则表达式
由于我们的问题是模式匹配问题,因此我们可以使用正则表达式:我们希望匹配字符串开头或结尾的所有零。
最重要的是,我们要删除那些匹配的零。换句话说,我们想要将它们替换为空字符串。
我们可以使用 String.replaceAll() 方法来做到这一点:
String removeLeadingZeroes(String s) {
return s.replaceAll("^0+", "");
}
String removeTrailingZeroes(String s) {
return s.replaceAll("0+$", "");
}
如果我们不想删除所有零,我们可以使用与 Apache Commons 和 Guava 相同的解决方案。然而,有一种纯正则表达式的方法可以做到这一点:我们必须提供一个模式,它不匹配整个字符串。
这样,如果输入仅包含零,则正则表达式引擎将从匹配中精确保留 1。我们可以通过以下模式来做到这一点:
String removeLeadingZeroes(String s) {
return s.replaceAll("^0+(?!$)", "");
}
String removeTrailingZeroes(String s) {
return s.replaceAll("(?!^)0+$", "");
}
注意,“(?!^)”和“(?!$)”分别表示它不是String的开头或结尾。
七、结论
在本教程中,我们了解了从字符串中删除前导和尾随字符的几种方法。这些实现之间的选择通常只是个人喜好。
与往常一样,这些示例可以在 GitHub 上找到。