2025, Sep 26 13:16
Как капитализировать имена, сохраняя пробелы и токены
Объясняем, как капитализировать имена в строках без нормализации пробелов: почему ломаются title/split-подходы и как реализовать надёжный алгоритм на Python.
Капитализация имён кажется пустяком, пока не добавишь строгие ограничения: сохранить каждый пробел точно как во входных данных и оставить без изменений токены, начинающиеся не с буквы. Это быстро исключает привычные помощники и выявляет крайние случаи вроде превращения “3g” в “3G” или схлопывания подряд идущих пробелов. Ниже — практичный путь к надёжному решению, которое проходит такие тесты, не нормализуя пробелы.
Постановка задачи
На вход подаётся строка из токенов, разделённых пробелами, например: “alex mcqueen”, “timothy karl logan” и “12abcd”. Нужно делать заглавной только первую букву каждого слова, если эта буква — алфавитный символ. Токены, начинающиеся с цифры или любого не-буквенного символа, менять нельзя, поэтому “12abcd” остаётся как есть. Исходные пробелы должны сохраниться.
Где спотыкается наивный подход
Типичная попытка — разбить по пробелам, обработать элементы и собрать строку обратно. Следующая функция демонстрирует эту идею и её хрупкость: она теряет точную раскладку пробелов и при сборке может добавить лишние пробелы в конце.
def reformat_tokens(src):
    acc = ""
    chunks = src.split(" ")
    for part in chunks:
        if part and part[0].isalpha():
            head = part[0].upper()
            acc += head
            for j in range(1, len(part)):
                acc += str(part[j])
        else:
            for j in range(0, len(part)):
                acc += str(part[j])
    total = len(chunks)
    seen = 0
    out = ""
    for pos in range(len(acc)):
        if pos != 0 and acc[pos].isupper() and seen != total:
            out += " "
            out += acc[pos]
            seen += 1
        else:
            pass
    return out
Разделяя по буквальному пробелу и пытаясь затем вернуть пробелы на места по границам заглавных, мы уходим от оригинальной раскладки. Отсюда и неожиданные лишние пробелы в конце.
Почему стандартные функции напрямую не помогают
Инстинктивно тянешься к стандартным функциям, но мешают два нюанса. Во‑первых, режим “title” делает заглавной букву после любого не-буквенного символа, поэтому “3g”.title() даёт “3G”, что нарушает требование оставить “12abcd” без изменений. Во‑вторых, капитализация по словам с помощью помощника, который токенизирует по пробельным символам, может нормализовать их. Например, из “john smith” получится “John Smith”: два подряд идущих пробела превратятся в один, если разделитель явно не указан. Если необязательный аргумент разделителя не передать, поведение окажется не тем, на которое рассчитываешь; указание разделителя, например один пробел, — уже другая история. Безопаснее работать на уровне символов.
Надёжный подход, который всё сохраняет
Правило на самом деле простое: делать букву заглавной только если она первая в строке или сразу следует за пробелом. Всё остальное копируем как есть. Так мы в точности сохраняем пробелы и уважаем токены, начинающиеся не с буквы.
def capitalize_words(raw):
    if len(raw) == 0:
        return raw
    buf = raw[0].upper() if raw[0].isalpha() else raw[0]
    for ch in raw[1:]:
        if buf[-1].isspace() and ch.isalpha():
            buf += ch.upper()
        else:
            buf += ch
    return buf
Эта логика фокусируется на единственном важном моменте — переходе от пробела к букве. Мы не выполняем повторную токенизацию, поэтому внутренние пробелы не меняются, а токены, начинающиеся с не-буквенных символов, остаются нетронутыми.
Зачем это важно
В соревновательном программировании и автотестах мельчайшие расхождения — лишний пробел или неожиданная заглавная после цифры — приводят к падениям. Опора на помощников, которые переформатируют пробелы или применяют расширенные правила “title”, может незаметно исказить данные. Проход по строке символ за символом — детерминированный, прозрачный и строго следует постановке.
Выводы
Если требуется сохранить исходную раскладку, избегайте разбиения и склейки по пробелам. Помните, что “title”-стиль может делать заглавной букву после цифр, а утилиты капитализации склонны сжимать несколько пробелов, если их явно не настроить. Минимальный проход по строке, который делает заглавными только буквы после пробела, — простой и надёжный способ для задач такого класса.
Статья основана на вопросе с StackOverflow от So Few Against So Many и ответе Ramrab.