Удалить ведущие и конечные символы из строки

Удалить ведущие и конечные символы из строки

1. Вступление

В этом коротком руководстве мы увидим несколько способов удалить начальные и конечные символы изString. Для простоты мы удалим нули в примерах.

В каждой реализации мы создадим два метода: один для начальных и один для конечных нулей.

Эта проблема имеет крайний случай: что мы хотим сделать, когда вход содержит только нули? Вернуть пустойString илиString, содержащий единственный ноль? Мы увидим реализации обоих вариантов использования в каждом из решений.

У нас есть модульные тесты для каждой реализации, которые вы можете найтиon GitHub.

2. ИспользуяStringBuilder

В нашем первом решении мы создадимStringBuilder with the original String, and we’ll delete the unnecessary characters с начала или с конца:

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(), когда удаляем завершающие нули, потому что он также удаляет последние несколько символов и более эффективен.

Если мыdon’t want to return an empty String, когда вход содержит только нули, единственное, что нам нужно сделать, этоstop the loop if there’s only a single character left.

Поэтому мы меняем условие цикла:

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()

В этом решении, когда мы удаляем начальные или конечные нули, мыfind the position of the first or last non-zero character.

После этого нам нужно только вызвать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 перед циклом for, потому что мы хотим использовать переменную вне области действия цикла.

Также обратите внимание, что нам нужно искать ненулевые символы вручную, посколькуString.indexOf() иString.lastIndexOf() работают только для точного соответствия.

Если мыdon’t want to return an empty String, мы должны сделать то же самое, что и раньше:change the loop condition:

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. зависимости

Мы можем использоватьApache Commons Lang3, вставив эту зависимость в наш файлpom.xml:


    org.apache.commons
    commons-lang3
    3.8.1

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 в качестве второго параметра. ЭтотString представляет собой набор символов, а не последовательность, которую мы хотим удалить.

Например, если мы передадим“01”, они удалят все начальные или конечные символы, то есть‘0' или‘1'.

5. Использование гуавы

Гуава также предоставляет много полезных классов. Для этой проблемы мы можем использоватьcom.google.common.base.CharMatcher, который предоставляет служебные методы для взаимодействия с совпадающими символами.

5.1. зависимости

Чтобы использовать Guava, мы должны добавить следующийdependencies в наш файлpom.xml:


    com.google.guava
    guava
    27.0.1-jre

Обратите внимание: если мы хотим использовать Guava в приложении Android, мы должны использовать версию27.0-android.

5.2. Реализация

В нашем случае нас интересуютtrimLeadingFrom() иtrimTrailingFrom().

Как следует из их названия, они удаляют любой начальный или конечный символ соответственно изString, который соответствуетCharMatcher:

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. Использование регулярных выражений

Поскольку наша проблема связана с сопоставлением с образцом, мы можем использовать регулярные выражения:we want to match all zeroes at the beginning or the end of aString.

Кроме того, мы хотим удалить эти совпадающие нули. Другими словами, мы хотимreplace them with nothing, or in other words, an empty String.

Мы можем сделать именно это с помощью методаString.replaceAll():

String removeLeadingZeroes(String s) {
    return s.replaceAll("^0+", "");
}

String removeTrailingZeroes(String s) {
    return s.replaceAll("0+$", "");
}

Если мы не хотим удалять все нули, мы можем использовать то же решение, которое мы использовали с Apache Commons и Guava. Однако есть способ сделать это чистым регулярным выражением: мы должны предоставить шаблон, который не соответствует всемуString.

Таким образом, если вход содержит только нули, механизм регулярных выражений будет исключать ровно один из совпадений. Мы можем сделать это с помощью следующих шаблонов:

String removeLeadingZeroes(String s) {
    return s.replaceAll("^0+(?!$)", "");
}

String removeTrailingZeroes(String s) {
    return s.replaceAll("(?!^)0+$", "");
}

Обратите внимание, что“(?!^)” и“(?!$)” означает, что это не начало и не конецString соответственно.

7. Заключение

В этом руководстве мы увидели несколько способов удалить начальные и конечные символы изString. Выбор между этими реализациями часто является просто личным предпочтением.

Как обычно доступны примерыover on GitHub.