العودة إلى المدونة

استكشاف الأخطاءدليل

UnicodeEncodeError عند طباعة رد LLM في Python: 3 حلول

هل ينهار print(reply) على السيريلية أو الإيموجي؟ اعرف لماذا يفشل Python مع المخرجات المُعاد توجيهها أو صفحة ترميز قديمة، وأصلحه بوضع UTF-8 أو reconfigure أو encoding=.

بواسطة CheapTokens Teamنُشر في 5 دقيقة قراءة

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 (الحالة نهائية، 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). خطأ الترميز لا علاقة له بالنموذج، فقط بالمكان الذي يذهب إليه النص.