Назад до блогу

Вирішення проблемПосібник

UnicodeEncodeError під час друку відповіді LLM у Python: 3 виправлення

print(reply) падає на кирилиці або емодзі? Дізнайтеся, чому Python зазнає невдачі з перенаправленим виводом або застарілою кодовою сторінкою, і виправте це за допомогою режиму UTF-8, reconfigure або encoding=.

Автор: CheapTokens TeamОпубліковано 6 хв читання

UnicodeEncodeError на print(reply) і виправлення UTF-8, намальовані як картка помилки
На цій сторінці
  1. Як виглядає помилка
  2. Перевірте, яке кодування у вас насправді
  3. Виправлення 1: увімкніть режим UTF-8
  4. Виправлення 2: переналаштуйте stdout всередині скрипта
  5. Виправлення 3: задайте файлам явне кодування
  6. Яке виправлення використовувати?
  7. Якщо все ще не працює

Ваш запит пройшов, модель відповіла, а потім Python впав на print(reply). API тут не винне. Відповідь нормальна; місце, куди ви її виводите, не може представити деякі її символи. Найшвидше виправлення — увімкнути режим UTF-8 за допомогою PYTHONUTF8=1. Якщо ви хочете залишити це всередині скрипта, викличте sys.stdout.reconfigure(encoding="utf-8") на початку, а коли зберігаєте відповідь у файл, передайте encoding="utf-8" у open().

Як виглядає помилка#

Припустімо, ви ставите моделі запитання російською і друкуєте відповідь. Ось весь скрипт, який використовує звичайний виклик requests з нашого посібника з першого запиту:

python
import os, requests

r = requests.post(
    "https://api.cheaptokens.dev/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['CHEAPTOKENS_API_KEY']}"},
    json={
        "model": "deepseek-ai/DeepSeek-V4-Flash-0731",
        "messages": [{"role": "user", "content": "Ответь одним предложением: что такое токен?"}],
        "max_tokens": 200,
    },
)
reply = r.json()["choices"][0]["message"]["content"]
print(reply)

Коли stdout перенаправлено у файл або канал, або Python запущено з застарілою кодовою сторінкою для його потоків, останній рядок закінчується так:

text
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-5: character maps to <undefined>

Назва кодека у вашому повідомленні залежить від кодування потоку (для кодової сторінки Windows це зазвичай charmap, для ascii — ascii), а позиції залежать від відповіді. Форма завжди однакова: трасування вказує на print, а не на запит.

Трасування UnicodeEncodeError вказує на print(reply)
Типова форма помилки: її викликає print, після того як виклик API вже успішно завершився

Чому це відбувається? print перетворює текст у байти, використовуючи кодування sys.stdout. Якщо це кодування є застарілою кодовою сторінкою, будь-який символ поза нею, наприклад кирилиця в західній кодовій сторінці, емодзі, іноді типографське тире, не має байта для перетворення. Python викликає помилку замість того, щоб вгадувати.

Це проявляється в трьох ситуаціях:

  • stdout перенаправлено або передано через канал (python app.py > out.txt, | another program), або скрипт запускається з планувальника, CI чи редактора, де stdout не є консоллю;
  • встановлено PYTHONLEGACYWINDOWSSTDIO, або потік інакше отримує вузьке кодування;
  • ви записуєте відповідь у файл за допомогою open(path, "w") без encoding=.

Який Python? Усе нижче стосується Python 3.7–3.14, де кодування потоків і файлів походить від системи. Згідно з PEP 686 (статус Final, https://peps.python.org/pep-0686/, перевірено 5 листопада 2026 року), режим UTF-8 увімкнено за замовчуванням з Python 3.15, тому open() і stdio там уже використовують UTF-8, якщо хтось не вимкнув його за допомогою PYTHONUTF8=0 або -X utf8=0. Перевірте за допомогою python --version.

Перевірте, яке кодування у вас насправді#

Перш ніж щось змінювати, подивіться, що вибрав Python. Це займе десять секунд.

Спробуйте саміСпробуйте: що Python вважає кодуванням вашого stdout?Показати відповідь

Запустіть python -c "import sys; print(sys.stdout.encoding)" у тому самому вікні, де скрипт зазнає невдачі. В інтерактивній консолі він зазвичай виводить utf-8, тож це саме по собі мало що доводить. Запустіть його знову з > check.txt в кінці та відкрийте файл: перенаправлений вивід частіше дає назву cp… або ascii, і це ваш винуватець. Якщо все каже utf-8, подивіться на файл, у який ви пишете.

Хочете побачити збій, не чекаючи поганого дня? На macOS або Linux примусово встановіть вузьке кодування для одного запуску:

bash
PYTHONIOENCODING=ascii python -c "print('Привет')"

Це завершується помилкою з повідомленням про кодек ascii, з тієї ж родини, що й помилка вище. У Windows змінна ігнорується для інтерактивної консолі, але працює, коли вивід перенаправлено: у cmd виконайте set PYTHONIOENCODING=ascii, потім python -c "print('Привет')" > out.txt (у PowerShell: $env:PYTHONIOENCODING = "ascii").

Виправлення 1: увімкніть режим UTF-8#

У Python є режим, який робить UTF-8 типовим для тексту. Згідно з документацією Python (сторінка командного рядка, перевірено 5 листопада 2026 року), PYTHONUTF8=1 вмикає його, а -X utf8 робить те саме; обидва існують з Python 3.7. Одна особливість із тієї ж сторінки: будь-яке інше непорожнє значення PYTHONUTF8 спричиняє помилку під час запуску, тому використовуйте саме 1 (або 0, щоб вимкнути його). У 3.15 це потрібно лише для явного ввімкнення режиму на старіших інтерпретаторах; якщо там він уже ввімкнений, шукайте PYTHONUTF8=0 у вашому середовищі.

Увімкніть режим UTF-8 для одного запуску

cmd

bat
set PYTHONUTF8=1
python app.py

PowerShell

powershell
$env:PYTHONUTF8 = "1"
python app.py

macOS / Linux

bash
PYTHONUTF8=1 python app.py

Без змін у коді, і це охоплює консоль, перенаправлення та файли в цьому запуску. Якщо ви не хочете встановлювати змінну щоразу, python -X utf8 app.py дає той самий результат з командного рядка.

Також є PYTHONIOENCODING=utf-8, який перевизначає кодування лише stdin, stdout і stderr. Прочитайте його запис у документації, перш ніж покладатися на нього в Windows: починаючи з Python 3.6, він ігнорується для інтерактивного буфера консолі, якщо не встановлено PYTHONLEGACYWINDOWSSTDIO. Перенаправлені файли та канали не підпадають під цей виняток. Тож він може виправити python app.py > out.txt і все одно нічого не робити у вікні консолі. Для stderr обробник помилок завжди backslashreplace.

Виправлення 2: переналаштуйте stdout всередині скрипта#

Якщо ви не можете контролювати, як люди запускають скрипт, змініть сам потік на самому початку:

python
import sys

sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")

print(reply)

reconfigure задокументовано для io.TextIOWrapper (додано в 3.7). Коли ви передаєте encoding і не вказуєте errors, обробником є strict, тому символ, який не підходить, все одно спричиняє помилку; для поширеного випадку UTF-8 нічого не залишається. Дві деталі з документації: кодування не можна змінити після того, як з потоку прочитано дані (можна після запису), а encoding="locale" підтримується з 3.11.

Потрібно, щоб скрипт ніколи не падав, навіть на терміналі, який справді не може показати текст? Додайте errors="replace". Невідомі символи тоді з'являться як ? замість зупинки запуску. Це приховує проблему, а не виправляє її, тому залиште це для журналів.

Виправлення 3: задайте файлам явне кодування#

Збереження відповіді — це та сама помилка, лише тихіша в коді:

python
# before: on Python up to 3.14 this uses the system's default encoding
with open("reply.txt", "w") as f:
    f.write(reply)

# after: the file is UTF-8 on every machine
with open("reply.txt", "w", encoding="utf-8") as f:
    f.write(reply)

Використовуйте той самий encoding="utf-8", коли читаєте файл назад, інакше текст може зламатися на вході.

До: print(reply) викликає UnicodeEncodeError. Після: той самий рядок друкує повну відповідь
Той самий рядок print, до і після режиму UTF-8

Яке виправлення використовувати?#

Оберіть свій випадок

Виберіть свій випадок

Я запускаю свій скрипт у терміналі

Встановіть PYTHONUTF8=1 один раз у своєму середовищі або запускайте з python -X utf8 app.py. У коді нічого не змінюється.

Я передаю скрипт іншим людям

Помістіть sys.stdout.reconfigure(encoding="utf-8") на початок, щоб він працював незалежно від того, як вони його запускають.

Збій у файлі, а не на екрані

Додайте encoding="utf-8" до open(), для запису та читання.

Ви можете комбінувати їх. Скрипт, який друкує та зберігає відповіді, потребує Виправлення 2 або Виправлення 1 для екрана та Виправлення 3 для файлу.

Якщо все ще не працює#

  • Помилка перемістилася на інший рядок. Тоді виклик, що зазнає невдачі, — це інший print або write; виправлення має застосовуватися до його запуску, тому помістіть reconfigure на самому початку.
  • Помилка згадує PYTHONUTF8. Змінна має значення, відмінне від 1 або 0. Видаліть її та встановіть знову.
  • Немає збою, але ви бачите квадратики або ? у консолі. Python записав коректний UTF-8; шрифт консолі або кодова сторінка не можуть його відобразити. Перенаправте у файл і відкрийте файл у редакторі, щоб переконатися, що відповідь ціла.
  • Помилка — це JSONDecodeError, а не UnicodeEncodeError. Інша проблема: дивіться як витягти JSON з відповіді моделі.

Код запиту вище — це звичайний виклик requests; документація CheapTokens показує той самий виклик для OpenAI SDK. Інша модель працює з тим самим виправленням: замініть поле model на zai-org/GLM-5.3-Flash (до 8 192 вихідних токенів на відповідь, контекст 390K). Помилка кодування не має нічого спільного з моделлю, лише з тим, куди йде текст.