3 คะแนน โดย GN⁺ 2024-04-28 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Python 3.15 จะเปิด โหมด UTF-8 เป็นค่าเริ่มต้น เพื่อให้การเข้ารหัสเริ่มต้นของไฟล์, standard input/output และ pipe เป็น UTF-8
  • UTF-8 ถูกใช้เป็น มาตรฐานโดยพฤตินัยของการเข้ารหัส ใน source file, JSON·TOML·YAML, ตัวแก้ไขข้อความหลัก, ข้อมูลบนเว็บ และภาษาอย่าง Node.js·Go·Rust·Java ทำให้การทำงานร่วมกันดีขึ้น
  • การเข้ารหัสเริ่มต้นเดิมแตกต่างกันไปตามแพลตฟอร์ม ดังนั้นหากนักพัฒนาบน Unix ละ encoding="utf-8" ออกไป ก็อาจเกิด บั๊กจากความไม่สอดคล้องกัน บน Windows และระบบอื่นได้
  • หากจำเป็นสามารถปิดได้ด้วย PYTHONUTF8=0 หรือ -X utf8=0 และสามารถใช้ EncodingWarning, encoding="utf-8", encoding="locale", locale.getencoding() เพื่อตรวจสอบความเข้ากันได้
  • โปรแกรมที่พึ่งพาการเข้ารหัสเริ่มต้นอาจเจอ UnicodeError, mojibake หรือความเสียหายของข้อมูลแบบเงียบ ๆ โดยเฉพาะบน Windows จึงควรตรวจสอบล่วงหน้า

การเข้ารหัสเริ่มต้นที่จะเปลี่ยนใน Python 3.15

  • PEP 686 คือการเปลี่ยนแปลงที่เปิดใช้ โหมด UTF-8 จาก PEP 540 เป็นค่าเริ่มต้น
  • สำหรับไฟล์, stdio และ pipe ที่ต้องใช้การเข้ารหัสเริ่มต้น Python จะใช้ UTF-8 อย่างสม่ำเสมอ
  • จะเปิดใช้เป็นค่าเริ่มต้นตั้งแต่ Python 3.15 เป็นต้นไป และผู้ใช้สามารถปิดได้ด้วยวิธีต่อไปนี้
    • PYTHONUTF8=0
    • -X utf8=0

เหตุผลที่ใช้ UTF-8 เป็นค่าเริ่มต้น

  • UTF-8 ได้กลายเป็นเสมือนมาตรฐานการเข้ารหัสข้อความในหลายสภาพแวดล้อม
    • การเข้ารหัสเริ่มต้นของไฟล์ source ของ Python คือ UTF-8
    • JSON, TOML, YAML ใช้ UTF-8
    • ตัวแก้ไขข้อความส่วนใหญ่ รวมถึง Visual Studio Code และ Windows Notepad ใช้ UTF-8 เป็นค่าเริ่มต้น
    • เว็บไซต์และข้อมูลข้อความบนอินเทอร์เน็ตส่วนใหญ่ใช้ UTF-8
    • ภาษาโปรแกรมยอดนิยมหลายภาษา เช่น Node.js, Go, Rust, Java ใช้ UTF-8 เป็นค่าเริ่มต้น
  • เมื่อการเข้ารหัสเริ่มต้นของ Python เปลี่ยนเป็น UTF-8 จะช่วยให้ การทำงานร่วมกัน กับเครื่องมือ ภาษา และรูปแบบข้อมูลอื่นดีขึ้น
  • นักพัฒนา Python บน Unix จำนวนมากมักลืมไปว่าการเข้ารหัสเริ่มต้นขึ้นกับแพลตฟอร์ม และละ encoding="utf-8" เมื่ออ่านข้อความ UTF-8 เช่น JSON·TOML·Markdown·ไฟล์ source ของ Python
  • ความแตกต่างของการเข้ารหัสเริ่มต้นในแต่ละแพลตฟอร์มจึงกลายเป็น สาเหตุของบั๊ก ที่ทำให้โค้ดเหล่านี้พังในสภาพแวดล้อมอื่น

การปรับ locale API และ encoding="locale"

  • โหมด UTF-8 มีผลต่อ locale.getpreferredencoding(False) ดังนั้นจึงจำเป็นต้องมี API ที่ใช้ดึงการเข้ารหัส locale โดยไม่ขึ้นกับโหมด UTF-8
  • locale.getencoding() ถูกเพิ่มเข้ามาเพื่อจุดประสงค์นี้ โดยจะคืนค่าการเข้ารหัส locale และไม่สนใจโหมด UTF-8
    • API นี้ถูกเพิ่มใน Python 3.11
  • หากกำหนดตัวเลือก warn_default_encoding ไว้ locale.getpreferredencoding() จะส่ง PEP 597 EncodingWarning เช่นเดียวกับ open()
  • PEP 597 ได้เพิ่มตัวเลือก encoding="locale" ให้กับ TextIOWrapper เพื่อให้สามารถระบุการเข้ารหัส locale อย่างชัดเจนได้
  • ก่อนหน้านี้ แม้จะระบุ encoding="locale" ในโหมด UTF-8 แต่ TextIOWrapper ก็ยังใช้ "UTF-8"
    • สิ่งนี้ไม่สอดคล้องกับแรงจูงใจของ PEP 597
    • เพราะไม่ได้คาดการณ์สถานการณ์ที่โหมด UTF-8 จะกลายเป็นค่าเริ่มต้นเมื่อการเข้ารหัสข้อความเริ่มต้นของ Python เปลี่ยนไป
  • ความไม่สอดคล้องนี้ถูกแก้ใน Python 3.11 และเมื่อส่ง encoding="locale" ในโหมด UTF-8 ก็จะใช้ การเข้ารหัส locale

ความเข้ากันได้ย้อนหลังและขั้นตอนการย้ายระบบ

  • ระบบ Unix ส่วนใหญ่ใช้ UTF-8 locale และ Python จะเปิดโหมด UTF-8 เมื่อ locale เป็น C หรือ POSIX ดังนั้นผลกระทบของการเปลี่ยนแปลงนี้จะไปกระจุกอยู่ที่ ผู้ใช้ Windows เป็นหลัก
  • โปรแกรม Python ที่พึ่งพาการเข้ารหัสเริ่มต้นอาจพบปัญหาดังต่อไปนี้
    • UnicodeError
    • mojibake
    • ความเสียหายของข้อมูลแบบเงียบ ๆ
  • ขั้นตอนที่แนะนำในการแก้ปัญหาความเข้ากันได้ย้อนหลังมีดังนี้
    1. ปิดโหมด UTF-8
    2. ใช้ EncodingWarning จาก PEP 597 เพื่อหาจุดที่โหมด UTF-8 ส่งผลกระทบ
      • หากละตัวเลือก encoding ไว้ ให้พิจารณาใช้ encoding="utf-8" หรือ encoding="locale"
      • หากใช้ locale.getpreferredencoding() ให้พิจารณาใช้ "utf-8" หรือ locale.getencoding()
    3. ทดสอบแอปพลิเคชันในโหมด UTF-8

กรณีนำมาก่อนของ Ruby·Java และทางเลือกที่ถูกปฏิเสธ

  • Ruby ได้เปลี่ยน external_encoding เริ่มต้นบน Windows เป็น UTF-8 ใน Ruby 3.0 เมื่อปี 2020
  • Java ได้เปลี่ยนการเข้ารหัสข้อความเริ่มต้นเป็น UTF-8 ใน JDK 18 เมื่อปี 2022
  • ทั้ง Ruby และ Java มีตัวเลือกเพื่อความเข้ากันได้ย้อนหลัง แต่ไม่มีคำเตือนการใช้การเข้ารหัสเริ่มต้นแบบ EncodingWarning ของ Python
  • แนวทางที่จะเลิกใช้การพึ่งพาการเข้ารหัสเริ่มต้นโดยสิ้นเชิงถูกปฏิเสธ
    • มีหลายกรณีที่ใช้การเข้ารหัสเริ่มต้นเพื่ออ่านและเขียนเฉพาะข้อความ ASCII
    • สำหรับแอปพลิเคชันที่รันบน Unix เท่านั้นและไม่ข้ามแพลตฟอร์ม คำเตือนลักษณะนี้ไม่ได้มีประโยชน์
    • หากบังคับให้ระบุ encoding ทุกที่ จะเพิ่มภาระให้ผู้ใช้มาก และ DeprecationWarning จำนวนมากอาจทำให้ผู้ใช้เมินคำเตือนไป
    • PEP 387 กำหนดให้เพิ่มคำเตือนเมื่อมีการเปลี่ยนแปลงที่กระทบความเข้ากันได้ย้อนหลัง แต่ไม่ได้บังคับว่าจะต้องเป็น DeprecationWarning
  • แนวทางที่จะใช้ PYTHONIOENCODING เป็นการเข้ารหัสเริ่มต้นของ pipe ในโมดูล subprocess ก็ถูกปฏิเสธเช่นกัน
    • วิธีนี้จะทำให้ยังใช้การเข้ารหัสแบบ legacy กับ subprocess.Popen(text=True) ได้ แม้อยู่ในโหมด UTF-8
    • แต่จะทำให้แนวคิดเรื่อง “การเข้ารหัสเริ่มต้น” ซับซ้อนขึ้น และตัววิธีเองก็เป็นการเปลี่ยนแปลงที่กระทบความเข้ากันได้ย้อนหลัง
    • ผู้ใช้สามารถปิดโหมด UTF-8 ไปก่อนจนกว่าจะเปลี่ยน text=True เป็น encoding="utf-8" หรือ encoding="locale"

มุมมองด้านการให้ความรู้ผู้ใช้

  • ผู้ใช้ใหม่จะมีความจำเป็นต้องเรียนรู้เรื่องการเข้ารหัสข้อความน้อยลงในช่วงปีแรก
  • ค่อยเรียนรู้เรื่องการเข้ารหัสเมื่อจำเป็นต้องจัดการกับไฟล์ข้อความที่ไม่ใช่ UTF-8
  • ผู้ใช้เดิมควรตรวจสอบจุดที่ได้รับผลกระทบตามขั้นตอนด้านความเข้ากันได้ย้อนหลัง

1 ความคิดเห็น

 
GN⁺ 2024-04-28
ความเห็นจาก Hacker News
  • การที่การเข้ารหัสไฟล์ข้อความแบบค่าปริยาย เปลี่ยนไปตามแพลตฟอร์ม เป็นเรื่องน่าหงุดหงิดมาโดยตลอด การเปลี่ยนครั้งนี้เลยน่ายินดี
    และก็ดีด้วยที่ไม่ได้พยายามไปยุ่งกับการเข้ารหัสของระบบไฟล์ เพราะนั่นเป็นอีกปัญหาหนึ่งและปวดหัวในแบบของมันเอง

    • code page ค่าปริยายของระบบบน Windows ไม่ได้ขึ้นอยู่แค่กับแพลตฟอร์ม แต่ยังขึ้นกับ system locale ด้วย
      การที่ Windows ไม่เปิดทางง่าย ๆ มานานให้เลือกใช้ code page แบบ UTF-8 กับฟังก์ชัน ANSI อย่าง TextOutA ถือเป็นความผิดพลาดครั้งใหญ่ กว่าจะทำได้ผ่านไฟล์ manifest ก็ช่วงกลาง ๆ ของการพัฒนา Windows 10 แล้ว ทั้งที่ฟีเจอร์แบบนี้ควรมีมาตั้งแต่ยุค NT4 หรือ Windows 98
    • ในเชิงประวัติศาสตร์มันก็สมเหตุสมผลอยู่ เพราะซอฟต์แวร์ส่วนใหญ่ใช้เฉพาะในท้องถิ่น และคาดว่าไฟล์ข้อความจะใช้การเข้ารหัสแบบท้องถิ่นด้วย
      มันไม่ได้ขึ้นกับแค่แพลตฟอร์ม แต่ยังขึ้นกับ locale ที่ผู้ใช้ตั้งไว้ด้วย และ C standard library ก็ทำงานแบบเดียวกัน ตัวอย่างเช่นบน Unix/Linux นั้น iso-8859-1 เคยพบได้บ่อยในภาษายุโรปตะวันตก และหลังมีการใช้เงินยูโร ก็มักเปลี่ยนไปเป็น iso-8859-15 ที่มีสัญลักษณ์ ด้วย UTF-8 เริ่มใช้งานได้แบบไม่มีปัญหาจริง ๆ ราวปลายทศวรรษ 2000 และ Debian ก็เปลี่ยนค่าปริยายเป็น UTF-8 ในรุ่น Etch
    • ไม่กี่วันก่อนก็เพิ่งโดนเรื่อง การเปลี่ยนรูปแบบขึ้นบรรทัดใหม่โดยปริยาย เล่นงาน
      ทดสอบในเครื่องที่ทำงานทุกอย่างผ่านหมด แต่พอ deploy ไปยังโฮสต์ Linux กลับใช้ไม่ได้ เพราะแอปพลิเคชันย่อยต้องการ CRLF จึงอ่านต่อไม่ได้ เป็นหนึ่งในปัญหาเล็ก ๆ งี่เง่าที่ต้องคอยจำไว้เป็นครั้งคราว แต่ก็เป็นคำถามที่สมเหตุสมผลเหมือนกันว่าทำไมซอฟต์แวร์ที่เขียนขึ้นใหม่ยังต้องบังคับใช้ตัวจบบรรทัดแบบใดแบบหนึ่ง
    • ใครก็ตามที่เริ่มเขียนโค้ดบน Windows มักจะโดนปัญหานี้เล่นงานหลายรอบ
  • การไม่พึ่งค่าปริยายของระบบที่ไว้ใจไม่ได้เป็นเรื่องที่ดี
    ค่าพวกนี้มักกลับมาเป็นอย่างอื่นจากที่เราคิดไว้ในสักช่วงหนึ่ง เมื่อหลายปีก่อนตอนจัดการ Ubuntu กับสคริปต์ init.d มีสคริปต์ที่รัน Java ในฐานะ root และยิ่งเป็นก่อนยุค Docker ก็ยิ่งเจอบ่อย มันถูกรันจากเชลล์ที่ไม่ได้ตั้งค่า UTF-8 ปกติให้ผู้ใช้ทั่วไป ผลก็คือไปเผยให้เห็นการใช้ API แย่ ๆ ของ Java ที่อาศัยค่าปริยายของ OS
    ทุกวันนี้ API ส่วนใหญ่มีรุ่นที่ระบุ encoding ได้ชัดเจน และเครื่องมือ static analysis ก็จะเตือนถ้าใช้ตัวที่ผิด แต่ถ้าพลาดไปแค่จุดเดียว เนื้อหาก็เริ่มเสียหายได้แล้ว ตอนนี้การใช้ encoding ที่ไม่ใช่ UTF-8 ส่วนมากมีโอกาสสูงมากว่าจะไม่ได้ตั้งใจ และถ้าตั้งใจจริงก็ไม่ควรไปพึ่งการตั้งค่าทางอ้อมประหลาด ๆ ของ OS แต่ควรระบุให้ชัดเจน ดังนั้นนี่จึงเป็น การเปลี่ยนแปลงที่ดี และถ้ามีโค้ดพังเพราะเรื่องนี้ การแก้แบบตรงไปตรงมาก็ดีกว่า

    • เคยใช้ .gitignore ที่สร้างจากฟังก์ชัน touch ซึ่งทำเป็น alias ใน PowerShell แต่ทำอย่างไรก็ Git ไม่ยอมสนใจ
      พอตรวจดูถึงพบว่าไฟล์ข้อความที่สร้างขึ้นเป็น UTF-16 เลยแทบจะถูกเมินไปโดยสิ้นเชิง จากนั้นก็ได้บทเรียนและเปลี่ยนค่าปริยายของระบบเป็น UTF-8 แต่ตอนนี้พึ่ง text editor ไปเลยง่ายกว่า
    • locale แบบ global นั้นเป็นความผิดพลาดโดยรวม ไม่ใช่แค่เรื่อง encoding
      ถ้า printf("%f", 4.2) แสดงผลเป็นสตริงคนละแบบแบบมีมนตร์ตาม environment มันก็สร้างปัญหามากกว่าช่วยแก้ปัญหา ถ้าต้องการพฤติกรรมที่อิง locale ก็ควรส่งข้อมูล locale หรือส่วนที่เกี่ยวข้องเข้าไปในฟังก์ชันอย่างชัดเจน
  • มี heuristic อย่างหนึ่งที่ยิ่งนานยิ่งแม่นขึ้นในช่วงหลายสิบปีที่ผ่านมา: ถ้ามีการตั้งค่า charset อยู่ที่ไหนสักแห่ง แล้ว ไม่ใช่ UTF-8 ก็แปลว่าผิด
    Python 2 ไม่ผูกกับชุดอักขระจึงดูเหมือนทำงานได้เสมอ แต่การปรับปรุงใน Python 3 ก็ไม่ได้เป็นแค่การปรับปรุงล้วน ๆ วิธีแยกสคริปต์ Python 3 ออกจาก Python 2 คือ ถ้ามีสตริง utf-8 อยู่ มันคือ Python 3 และถ้ารันได้เฉพาะใน locale C.UTF-8 มันก็คือ Python 3 การเปลี่ยนครั้งนี้จึงน่ายินดีเพราะเหมือนเป็นการ “ซ่อม” Python 3

  • นึกว่าตั้งแต่ Python 3 เป็นต้นมามันเป็นค่าปริยายอยู่แล้ว

    • น่าจะนึกถึงสตริงใน Python 3 ที่ไม่ต้องมีคำนำหน้า u"" แล้ว
      เมื่อกี้ลองพิมพ์ "éķů" ใน Python 2.7 ก็ได้ไบต์ UTF-8 ของตัวอักษรนั้นออกมา เลยไม่แน่ใจนักว่าคำนำหน้า u ทำอะไรแน่ แต่หนึ่งในการเปลี่ยนแปลงใหญ่ตอนย้ายจาก Python 2 ไป Python 3 คือสตริงมี encoding ส่วน byte string กลายเป็นลำดับไบต์ที่ไม่มี encoding การเปลี่ยนครั้งนี้ดูจะเกี่ยวหลัก ๆ กับปัญหาที่ในสภาพแวดล้อมอย่าง Windows ซึ่ง encoding ค่าปริยายไม่ใช่ UTF-8 นั้น เวลาจะใช้ open('filename', mode='r') ต้องระบุ open('filename', mode='r', encoding='UTF-8') เอง
    • ใน Python 3 นั้น ซอร์สโค้ด Python ใช้ UTF-8 เป็นค่าปริยายอยู่แล้ว แต่ไม่ได้พูดอะไรเลยเกี่ยวกับการเข้ารหัสอักขระที่ใช้ตอนบันทึกลงไฟล์ และค่าปริยายตรงนั้นยังขึ้นกับ locale
      อย่าง Path("filenames use their own encoding").write_text("file content encoding uses yet another encoding") นั้น encoding ของ string literal, ชื่อไฟล์ และเนื้อหาไฟล์เป็นคนละส่วนกันทั้งหมด โดย encoding ที่สอดคล้องกันคือ UTF-8 ของ tokenize.open, sys.getfilesystemencoding() ของ os.fsencode และ locale.getpreferredencoding() ของ open
  • ที่บอกว่า “ภาษาโปรแกรมยอดนิยมอื่น ๆ รวมถึง Node.js, Go, Rust, Java ก็ใช้ UTF-8 เป็นค่าปริยายเช่นกัน” นี่ทำเหมือนกับว่าผมพลาดตอนที่ Java ย้ายจาก UTF-16 ไป UTF-8

    • ค่า encoding ปริยายเวลาแปลงไบต์เป็นสตริงใน Java เดิมทีขึ้นกับแพลตฟอร์ม แต่ตอนนี้เป็น UTF-8 แล้ว
      ภายในคลาส String ยังใช้การเข้ารหัสแบบ UTF-16 และ latin-1 อยู่ และ JVM ก็ยังใช้การเข้ารหัสแบบ modified UTF-8 เหมือนเดิม เดิมทีคลาส String ใช้แต่ UTF-16 อย่างเดียว แต่ตั้งแต่ Java 9 เป็นต้นมา ถ้าทำได้ก็จะใช้ latin-1 แบบ 1 ไบต์ต่ออักขระด้วย
    • ดูเหมือนกำลังพูดปนกันระหว่างรูปแบบแทนสตริงภายในกับ encoding ที่ใช้ตอนอ่าน/เขียน
      Java ไม่เคยใช้ UTF-16 เป็นค่าปริยายสำหรับ encoding ตอนอ่าน/เขียน
    • ดูเหมือนจะเปลี่ยนใน Java 18 เมื่อ 2 ปีก่อน
  • การเข้ารหัสภายในของ CPython ตอนนี้เป็น UTF-8 แล้วหรือ?
    สตริงของ Python สามารถเข้าถึงด้วยดัชนีได้ แต่การเข้าถึงแบบสุ่มเกิดขึ้นไม่บ่อยพอสมควร จึงน่าจะพอรับได้หากทำดัชนีแบบหน่วงเวลาเมื่อจำเป็น แค่เลื่อนไปข้างหน้าหรือถอยหลังทีละตำแหน่งก็ไม่ต้องใช้ดัชนี ดังนั้นการใช้ UTF-8 เป็นรูปแบบภายในก็เป็นไปได้เพียงพอ

    • สิ่งที่ใช้แทน str คืออ็อบเจ็กต์ PyUnicode
      เมื่อมีการขอไบต์ UTF-8 ระบบจะสร้างอ็อบเจ็กต์ bytes ขึ้นเมื่อจำเป็น แล้วแคชไว้เป็นส่วนหนึ่งของ PyUnicode และจะถูกปล่อยไปพร้อมกันเมื่อ PyUnicode ถูกคืนหน่วยความจำ ส่วน code point ที่ประกอบเป็นสตริงจะถูกเก็บแยกไว้ในอาร์เรย์ธรรมดาเพื่อให้เข้าถึงแบบสุ่มได้ แต่ละ code point อาจใช้ขนาด 1, 2 หรือ 4 ไบต์ และตอนสร้าง PyUnicode ถ้าระบุค่า code point สูงสุดไว้ ระบบจะปัดขึ้นเป็นหนึ่งใน 127, 255, 65535, 1,114,111 เพื่อกำหนดว่าจะใช้ 1/2/4 ไบต์
      ถ้าค่า code point สูงสุดเป็น 127 ก็สามารถใช้อาร์เรย์นั้นเป็น UTF-8 ได้โดยตรง ดังนั้นคำตอบของคำถามนี้คือ สตริงจำนวนมากที่มีทุก code point ไม่เกิน 127 จะถูกเก็บเป็น UTF-8 อย่างไรก็ตาม เวลาวนผ่านสตริงไม่ควรทำในระดับ code point เพราะอักขระที่ผู้ใช้รับรู้ หรือ grapheme cluster อาจประกอบด้วย code point หนึ่งตัวหรือมากกว่า ตัวอย่างเช่น e ที่มีเครื่องหมายกำกับเสียงอาจเป็น code point e ตามด้วย code point ของเครื่องหมายกำกับเสียงแบบผสม และอีโมจินกฟีนิกซ์ประกอบด้วยอีโมจินก ตัวเชื่อมแบบไม่มีความกว้าง และอีโมจิไฟ ระบบอักษรบางแบบที่มีผู้ใช้นับร้อยล้านคนก็คล้ายกัน คือมีเครื่องหมายผสมเพื่อบอกสระติดกับพยัญชนะ ข้อความ - - นี้มี 5 code point และมีบทความที่ดีว่าภาษาแต่ละภาษารายงาน “ความยาว” ของมันอย่างไร: https://hsivonen.fi/string-length/. เนื้อหาส่วนนี้มาจากประสบการณ์ที่เพิ่งลองทำ Unicode TR29 เป็น Python C extension
  • สงสัยว่าทำไมไม่ใช้ utf-8-sig มันรองรับ BOM แบบเลือกได้ด้วย และเมื่อสัปดาห์ก่อนก็ยังต้องแก้สคริปต์เพราะเรื่องนี้อยู่เลย

    • ตอนนี้ไม่ควรใส่ BOM ใน UTF-8 กับอะไรทั้งนั้นแล้ว
      ไม่ใช่แนวทางที่แนะนำ และทุกวันนี้ก็ถือว่าสมเหตุสมผลด้วยซ้ำหากการทำงานบางอย่างล้มเหลวเพราะ BOM
    • การเปลี่ยนให้ Python แอบเติม BOM ที่มองไม่เห็นไว้หน้าข้อมูลเข้าออกทั้งหมดคงไม่ใช่ความคิดที่ดี
  • ถ้าพูดถึง UTF-8, Linux framebuffer ก็ควรมีการรองรับ UTF-8 ที่ถูกต้องมาตั้งนานแล้ว
    ไม่ใช่แบบ 256/512 glyph แต่เป็นการรองรับจริง GNU Hurd เองยังมีเทอร์มินัลคอนโซลที่ดีกว่าซึ่งรองรับ UTF-8 มาตั้งแต่ราวปี 2007 แล้ว แต่นี่ปี 2024 แล้วนะ

  • ดีเลย ตอนนี้ก็เหลือแค่ JS เปลี่ยนมาใช้ UTF-8 เท่านั้น
    แน่นอนว่า JS ปรับปรุงไม่ได้ เพราะต่างจากภาษาโปรแกรมอื่น ๆ มันต้องเข้ากันได้กับโค้ดที่เขียนไว้ตั้งแต่ปี 1995

    • ประเด็นนี้พูดถึงว่าเมื่อขอให้ Python เปิดไฟล์เป็น “ข้อความ” จะใช้อะไรเป็น encoding ปริยาย
      รูปแบบภายในของสตริงเป็นอีกเรื่องหนึ่ง และเหมือนกับ JavaScript ที่ Python ก็ไม่ได้ใช้ “UTF-8 ล้วน ๆ” ภายใน
  • จากประโยคที่ว่า “นักพัฒนา Python จำนวนมากที่ใช้ Unix ลืมไปว่าการเข้ารหัสปริยายขึ้นอยู่กับแพลตฟอร์ม จึงละ encoding=\"utf-8\" ออกเวลาอ่านไฟล์ข้อความที่เข้ารหัสเป็น UTF-8” อาจไม่ใช่ว่าลืม แต่อาจเป็นเพราะเรื่องนี้ไม่เป็นที่รู้กันมากพอ
    พูดตามตรง ฉันคิดมาตลอดว่า Python ใช้ UTF-8 ทุกที่อยู่แล้ว เว้นแต่จะขออย่างชัดเจนให้เป็นอย่างอื่น

    • ความจริงแล้วขึ้นอยู่กับกรณี
      bytes.decode และ str.encode ใช้ UTF-8 เป็นค่าปริยายมาตั้งแต่ Python 3 เป็นอย่างน้อย ในทางกลับกัน encoding ปริยายที่ใช้ถอดรหัสชื่อไฟล์จะใช้ sys.getfilesystemencoding() ซึ่งบน Windows และ macOS ก็เป็น UTF-8 เช่นกัน แต่บน Linux จะขึ้นอยู่กับ locale โดยเฉพาะ CODESET สุดท้าย open จะใช้ locale.getencoding() โดยตรง