กลับไปที่บล็อก

การแก้ไขปัญหาคู่มือ

UnicodeEncodeError เมื่อพิมพ์คำตอบ LLM ใน Python: 3 วิธีแก้

print(reply) แครชบนอักษรซีริลลิกหรืออีโมจิ? ดูว่าทำไม Python ล้มเหลวกับเอาต์พุตที่ถูกเปลี่ยนเส้นทางหรือโค้ดเพจแบบเก่า และแก้ด้วยโหมด UTF-8, reconfigure หรือ encoding=

โดย CheapTokens Teamเผยแพร่เมื่อ ใช้เวลาอ่าน 7 นาที

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>

ชื่อ codec ในข้อความของคุณขึ้นอยู่กับการเข้ารหัสของสตรีม (สำหรับโค้ดเพจ Windows มักเป็น charmap สำหรับ ascii คือ ascii) และตำแหน่งขึ้นอยู่กับคำตอบ รูปแบบเหมือนเดิมเสมอ: traceback ชี้ไปที่ print ไม่ใช่ที่คำขอ

traceback ของ 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('Привет')"

มันล้มเหลวด้วยข้อความ codec 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) ข้อผิดพลาดการเข้ารหัสไม่เกี่ยวข้องกับโมเดล เกี่ยวข้องกับที่ที่ข้อความไปเท่านั้น