Как получить "голый" текст, без маски?

Рейтинг: 1Ответов: 2Опубликовано: 02.05.2023

Есть маска текста: xxx/13xx/xxx
Есть текст: 222/1300/777
Как я могу получить текст 22200777 ?

.replace("/13", "") отработает криво на примере 222/1300/135

Ответы

▲ 3Принят

Через регулярные выражения: заменяем все иксы на (.), дальше матчим как регулярное выражение, вытаскиваем все группы:

String mask = "xxx/13xx/xxx";
var pattern = Pattern.compile("^" + mask.replace("x", "(.)") + "$");
String text = "222/1300/777";

var match = pattern.matcher(text);
if (match.find()) {
    var stringBuilder = new StringBuilder();
    for (int i = 1; i <= match.groupCount(); i++) {
        stringBuilder.append(match.group(i));
    }
    String result = stringBuilder.toString();
    System.out.println(result);
} else {
    System.out.println("Ничего не найдено (строка не соответствует маске)");
}

Вывод:

22200777

Другой вариант - просто идем циклом, сравнивамем символы маски и текста, то что соответствует иксам - добавляем в результат (тут в коде получилось больше валидации, чем какой-то сложной логики):

private static String getByMask(String text, String mask) {
    var result = new StringBuilder();

    if (text.length() != mask.length()) {
        throw new RuntimeException("Длина текста не соответствует маске");
    }

    for (int i = 0; i < mask.length(); i ++ ) {
        if (mask.charAt(i) == 'x') {
            result.append(text.charAt(i));
        } else if (mask.charAt(i) != text.charAt(i)) {
            throw new RuntimeException(
                    String.format("Символ на позиции %d ('%c') не соответствует маске (символ '%c')",
                            i, text.charAt(i), mask.charAt(i)));
        }
    }
    return result.toString();
}

public static void main(String[] args) {
    String mask = "xxx/13xx/xxx";
    String text = "222/1300/777";
    System.out.println(getByMask(text, mask));
}
▲ 2

Варианты с использованием Stream API и методами Matcher::results() / Matcher::replaceAll()

  1. Аналог предложенного решения:
  • символы x в маске превращаются в группы (.)
  • при совпадении для каждой группы берётся её значение по номеру
public static String checkMask1(String mask, String val) {
    return Pattern.compile(mask.replaceAll("x", "(.)"))
        .matcher(val)
        .results() // Stream<MatchResult>
        .flatMap(mr -> IntStream.rangeClosed(1, mr.groupCount())
            .mapToObj(i -> mr.group(i)) 
        ) // Stream<String>
        .collect(Collectors.joining());
}

При несоответствии шаблона возвращается пустая строка.

  1. Вариант похожий на решение от @Akina в комментариях:
  • Аналогично строится паттерн, в котором x меняется на (.)
  • Строится выражение для замены всех x на "$1$2...$N" с использованием Matcher::replaceAll, все символы не-x удаляются.
  • Используется "обычная" замена в строке
public static String checkMask2(String mask, String val) {
    int[] ix = {1};

    return val.replaceFirst(
        mask.replaceAll("x", "(.)"),
        Pattern.compile("x").matcher(mask.replaceAll("[^x]", ""))
            .replaceAll(mr -> "\\$"+ix[0]++)
    );
}

При несоответствии шаблона возвращается исходная строка

String[] tests = {"222/1300/777", "222/1400/777"};
for (String t : tests) {
    System.out.println(t + " -> '" + checkMask1("xxx/13xx/xxx", t) + "'");
    System.out.println(t + " -> '" + checkMask2("xxx/13xx/xxx", t) + "'");
}
222/1300/777 -> '22200777'
222/1300/777 -> '22200777'
222/1400/777 -> ''
222/1400/777 -> '222/1400/777'