«1. Введение

В этом уроке мы рассмотрим, как сортировать буквенно-цифровые строки по содержащимся в них числам. Мы сосредоточимся на удалении всех нечисловых символов из строки перед сортировкой нескольких строк по оставшимся числовым символам.

Мы рассмотрим распространенные пограничные случаи, включая пустые строки и недопустимые числа.

Наконец, мы проведем модульное тестирование нашего решения, чтобы убедиться, что оно работает должным образом.

2. Описание проблемы

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

  1. Our strings may contain only numbers, only letters or a mix of the two.
  2. The numbers in our strings may be integers or doubles.
  3. When numbers in a string are separated by letters, we should remove the letter and condense the digits together. For example, 2d3 becomes 23.
  4. For simplicity, when an invalid or missing number appears, we should treat them as 0.

Установив это, давайте приступим к нашему решению.

3. Решение с использованием регулярных выражений

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

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

String DIGIT_AND_DECIMAL_REGEX = "[^\\d.]"

String digitsOnly = input.replaceAll(DIGIT_AND_DECIMAL_REGEX, "");

Давайте кратко объясним, что происходит:

  1. ‘[^ ]’ –  denotes a negated set, therefore targetting any character not specified by the enclosed regex
  2. ‘\d’ – match any digit character (0 – 9)
  3. ‘.’ – match any “.” character

Затем мы используем метод String.replaceAll для удаления любых символов, не указанных в нашем регулярном выражении. Делая это, мы можем гарантировать, что первые три пункта нашей цели могут быть достигнуты.

Далее нам нужно добавить некоторые условия, чтобы убедиться, что пустые и недопустимые строки возвращают 0, а допустимые строки возвращают допустимый тип Double:

if("".equals(digitsOnly)) return 0;

try {
    return Double.parseDouble(digitsOnly);
} catch (NumberFormatException nfe) {
    return 0;
}

Это завершает нашу логику. Все, что осталось сделать, это подключить его к компаратору, чтобы мы могли удобно сортировать списки входных строк.

Давайте создадим эффективный метод для возврата нашего компаратора откуда угодно:

public static Comparator<String> createNaturalOrderRegexComparator() {
    return Comparator.comparingDouble(NaturalOrderComparators::parseStringToNumber);
}

4. Тестировать, тестировать, тестировать

Что хорошего в коде без тестов для проверки его функциональности? Давайте настроим быстрый модульный тест, чтобы убедиться, что все работает так, как мы планировали:

List<String> testStrings = 
  Arrays.asList("a1", "d2.2", "b3", "d2.3.3d", "c4", "d2.f4",); // 1, 2.2, 3, 0, 4, 2.4

testStrings.sort(NaturalOrderComparators.createNaturalOrderRegexComparator());

List<String> expected = Arrays.asList("d2.3.3d", "a1", "d2.2", "d2.f4", "b3", "c4");

assertEquals(expected, testStrings);

В этом модульном тесте мы упаковали все запланированные сценарии. Недопустимые числа, целые числа, десятичные дроби и числа, разделенные буквами, включены в нашу переменную testStrings.

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

В этой короткой статье мы продемонстрировали, как сортировать буквенно-цифровые строки на основе чисел внутри них, используя регулярные выражения, которые делают за нас тяжелую работу.

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

Как всегда, код можно найти на GitHub.