2026, Jan 01 06:01

Как генерировать пароли в Python без пробелов и управляющих символов

Разбираем, почему string.printable даёт переводы строк в паролях, и показываем безопасный генератор на Python: исключаем пробел и управляющие символы [:-6].

При генерации паролей в Python из широкого набора символов легко случайно захватить управляющие символы вроде перевода строки или табуляции. В итоге строка печатается на нескольких строках, засоряет логи и её трудно читать. Если вы берёте символы из string.printable, как раз сталкиваетесь с этой проблемой.

Обзор проблемы

Простейший генератор может выглядеть так:

import random
import string
passcode = ''.join(
    random.choices(string.printable, k=random.randint(8, 15))
).replace(' ', '')

Этот подход постфактум убирает пробелы, но по-прежнему пропускает символы вроде \n, \t, \r, \x0b и \x0c. Посмотрите, как ведёт себя значение с переводом строки:

raw_a = '57+%\n:cz'
shown_a = '57+%\\n:cz'

Первая строка содержит настоящий перевод строки, вторая — это тот же набор символов, только записанный так, чтобы символ перевода строки был виден как \n.

Что на самом деле происходит

Дело не в «символах экранирования» внутри пароля, а в управляющих символах, входящих в string.printable. Если посмотреть на эту константу, в ней есть цифры, буквы, знаки пунктуации, пробел, а в конце — печатные управляющие символы:

'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

Эти замыкающие символы — пробел, за которым следуют табуляция, перевод строки, возврат каретки, вертикальная табуляция и перевод страницы. Если их разрешать, пароли неизбежно будут занимать несколько строк или содержать невидимые пробельные символы.

Важно также отличать представление строки от её значения. Обратная косая черта — это символ экранирования. В пароле обратная косая черта будет записана как 'abc\\def' в представлении, но печататься и сравниваться как abc\def. Эта тонкость заметна при выводе:

print('\'', len('\''))
print('\\', len('\\'))

Вывод покажет одинарную кавычку и один обратный слэш, и у каждого длина 1.

Решение

Проще всего выбирать символы из подмножества string.printable, исключив пробел и управляющие символы. Это как раз последние шесть элементов константы, поэтому срез на источнике полностью устраняет проблему:

import random
import string
safe_passcode = ''.join(
    random.choices(string.printable[:-6], k=random.randint(8, 15))
)

Срез [:-6] удаляет ровно шесть символов в конце: пробел и пять управляющих символов. Исключив их заранее, вы гарантируете, что сгенерированный пароль не будет содержать пробелов и управляющих символов. Вдобавок вы не рискуете случайно укоротить пароль ниже минимальной длины при последующем удалении пробелов.

Если вывести отфильтрованный пул, вы увидите только видимые символы:

print(string.printable[:-6])
# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

Почему это важно

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

Дополнительные соображения

Практично ещё сильнее сузить набор: взять string.ascii_letters + string.digits и, при необходимости, несколько знаков пунктуации — например, точку и подчёркивание. Либо просто исключить обратный слэш, если он усложняет рабочий процесс.

Вывод

Создавая генератор паролей на Python, явно задавайте допустимый набор символов. Если сейчас используете string.printable, отрежьте завершающие пробел и управляющие символы через string.printable[:-6], чтобы гарантировать однострочный, чистый вывод. Понимайте разницу между представлением строки и её реальным значением, особенно в части обратных слэшей, и при необходимости сужайте пул до чётко определённого подмножества.