Как распарсить строку, которая имеет дробные числа

Имеется к примеру вот такой текст Это стоит 11 бакс, 5.0 а 110 вот это 1.1 - 12. Нужно целые числа заменить словами. Т.е. итог должен выглядеть таким образом Это стоит одиннадцать бакс, 5.0 а сто десять вот это 1.1 - двенадцать. Не понимаю какую регулярку применить, чтобы двенадцать тоже учитывало, т.к. там конец предложения и получается 12.


Ответы (1 шт):

Автор решения: Alex Rudenko

Регулярное выражение может выглядеть так:

Pattern num = Pattern.compile("(?<!\\.)\\d+(?!\\.\\d+)");
  • (?<!\\.) - look-behind: чтобы не было десятичной точки перед последовательностью цифр
  • \\d+ - последовательность из минимум одной цифры
  • (?!\\.\\d+) - look-ahead: после цифр нет точки, за которой идёт последовательность цифр

Для замены чисел на слова начиная с Java 9 можно применить метод Matcher::replaceAll(Function<MatchResult, String> replacer), позволяющий преобразовать совпадение в регулярке в нужное значение, т.е. в данном случае число в слова.

Например, функция translate для преобразования чисел в диапазоне [0, 999] может быть такой:

static final String[] below20 = {
    "ноль", "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять",
    "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать", 
    "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"
};
static final String[] tens = {
    "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто"
};
static final String[] hunds = {
    "сто", "двести", "триста", "четыреста", "пятьсот", "шестьсот", "семьсот", "восемьсот", "девятьсот"
};

static String translate(int i) {
    if (i == 0) return below20[0];
    StringBuilder sb = new StringBuilder();
    if (i >= 100) {
        sb.append(hunds[i / 100 - 1]);
    }
    int p = i % 100;
    if (sb.length() > 0 && p > 0) {
        sb.append(' ');
    }
    if (p < 20) {
        sb.append(below20[p]);
    } else {
        sb.append(tens[p/10 - 2]).append(' ').append(below20[p % 10]);
    }
    return sb.toString();
}

Для упрощения вызова translate как ссылки на метод, можно написать адаптер:

static String translate(MatchResult mr) {
    return translate(Integer.parseInt(mr.group()));
}

Тест:

String s = "Это стоит 11 бакс, 5.0 а 110 вот это 1.1 - 12.";

Pattern num = Pattern.compile("(?<!\\.)\\d+(?!\\.\\d+)");
// теcтовый вывод совпадений регулярки
num.matcher(s).results() // Stream<MatchResult>
    .map(MatchResult::group) // Stream<String>
    .forEach(System.out::println);

String words = num.matcher(s).replaceAll(MyClass::translate);

System.out.println(words);

Результат:

11
110
12
Это стоит одиннадцать бакс, 5.0 а сто десять вот это 1.1 - двенадцать.
→ Ссылка