Правильная сортировка строк, содержащих числа в начале

При стандартной лексикографической сортировке строк, начинающихся с чисел, возникает проблема: система сравнивает их посимвольно, что приводит к некорректному порядку. Например, строки "1 строка", "2 строка", "11 строка" будут расположены как "1 строка", "11 строка", "2 строка", поскольку сравнение происходит по первому символу "1" и "2".

Требуется реализовать сортировку, при которой:

  • Сначала извлекается и учитывается полное числовое значение в начале строки.
  • Если число отсутствует, сравнение происходит по алфавиту.
  • Ожидаемый результат: "1 строка", "2 строка", "11 строка".

Проблема текущего решения

Предложенный код использует сравнение только первых символов:

usort($subcats, function ($a, $b) {
    return strcmp(mb_substr($a->name, 0, 1), mb_substr($b->name, 0, 1));
});

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

Универсальное решение

Для корректной обработки необходимо реализовать функцию сравнения, которая:

  1. Извлекает числовой префикс из каждой строки.
  2. Сравнивает строки сначала по числовому значению (если оно присутствует в обеих).
  3. Если числа нет или они равны, переходит к лексикографическому сравнению.

Вот пример реализации на PHP с использованием функции usort и пользовательского компаратора:

usort($subcats, function ($a, $b) {
    // Извлечение чисел из начала строк
    preg_match('/^\d+/', $a->name, $matchesA);
    preg_match('/^\d+/', $b->name, $matchesB);
    
    $numA = $matchesA[0] ?? null;
    $numB = $matchesB[0] ?? null;
    
    // Если оба значения - числа, сравниваем их численно
    if ($numA !== null && $numB !== null) {
        if ($numA != $numB) {
            return $numA <=> $numB;
        }
        // Если числа равны, сравниваем полные строки лексикографически
        return strcmp($a->name, $b->name);
    }
    
    // Если только одно из значений - число, число должно идти первым
    if ($numA !== null && $numB === null) {
        return -1;
    }
    if ($numA === null && $numB !== null) {
        return 1;
    }
    
    // Если чисел нет в обеих строках, стандартное строковое сравнение
    return strcmp($a->name, $b->name);
});

Этот алгоритм обеспечивает естественную сортировку (natural sort), где числовые префиксы обрабатываются как целые числа, а не как последовательности символов.

Альтернативные подходы

  • Использование функции natsort() или natcasesort() для массивов, если требуется сортировка по значениям.
  • Применение флага SORT_NATURAL в функциях типа sort() или asort().
  • Для более сложных сценариев (например, числа внутри текста) можно рассмотреть функцию strnatcmp().

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