从字符串中删除前导和尾随字符

评论 0 浏览 0 2018-12-04

一、简介

在这个简短的教程中,我们将看到几种从字符串中删除前导和尾随字符的方法。为了简单起见,我们将删除示例中的零。

对于每个实现,我们将创建两种方法:一种用于前导,另一种用于尾随零。

这个问题有一个边缘情况:当输入仅包含零时我们想要做什么?返回一个空的字符串,还是一个包含单个零的字符串?我们将在每个解决方案中看到这两个用例的实现。

我们为每个实现提供了单元测试,您可以找到 在 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 上找到。

最后更新2024-01-03
0 个评论