φυβλαςのβλογ
phyblas的博客



unicode และ ASCII ใน python 2.x และ 3.x
เขียนเมื่อ 2015/12/19 17:51
แก้ไขล่าสุด 2024/01/22 21:33
บทความนี้แตกออกมาจาก https://phyblas.hinaboshi.com/20151217



ในการเปลี่ยนแปลงจากไพธอน 2 ไปสู่ไพธอน 3 นั้นมีอะไรเปลี่ยนแปลงไปหลายอย่าง บางเรื่องก็เปลี่ยนไปแค่เล็กน้อยเท่านั้น

แต่สำหรับเรื่องของสายอักขระ (string) นั้น ถือเป็นเรื่องหนึ่งที่ได้เกิดความเปลี่ยนแปลงขึ้นมาพอสมควร

ในไพธอน 2 นั้นข้อมูลสายอักขระถูกเก็บในรูป ASCII เป็นหลัก ในขณะที่ในไพธอน 3 ข้อมูลถูกเก็บในรูปยูนิโค้ดชนิด utf-8

แม้ว่าความเปลี่ยนแปลงจะดูเผินๆเหมือนไม่ได้เห็นผลชัดเจน แต่หากทำงานที่ต้องมีการจัดการกับตัวอักษรความต่างนี้ก็อาจทำให้เกิดความผิด พลาดคลาดเคลื่อนได้ จึงมีความสำคัญที่จะต้องกล่าวถึงอยู่



ในไพธอน 3 ชนิดของตัวอักษรที่เก็บอยู่ภายในสายอักขระนั้นทั้งหมดถูกเก็บในรูปแบบของ ยูนิโค้ด (unicode) ซึ่งสามารถเก็บตัวอักษรทั้งหมดทุกชนิดได้ในลักษณะเดียวกัน

แต่ในไพธอน 2 อักษรถูกเก็บในรูปของ ASCII ส่วนอักษรที่ไม่ใช่อักษร ASCII ถูกเก็บในรูปของรหัสยูนิโค้ด utf-8

ลองใช้ฟังก์ชัน len เพื่อหาความยาวของสายอักขระที่มีอักษรที่ไม่ใช่ ASCII ดู เปรียบเทียบกันระหว่างในไพธอน 2 กับ 3

print(len('abcedกขคงจ'))

ในไพธอน 3 จะได้ผลออกมาเป็น

10
ซึ่งก็เป็นไปตามที่เห็น เพราะความยาวของสายอักขระที่ใส่ลงไปเป็น ๑๐ ตัวอักษร

แต่ถ้าเป็นในไพธอน 2 จะได้เป็น
20
ที่เป็นแบบนี้ทั้งๆที่มีอักษรแค่ ๑๐ ตัวเท่านั้นก็ เพราะว่าในไพธอน 2 อักษรถูกเก็บในรูปของ ASCII อักษรที่ไม่ใช่ ASCII จะถูกเก็บในรูปของรหัสยูนิโค้ดซึ่งแต่ละอักษรก็ใช้จำนวนโค้ดแตกต่างกันออกไป

สำหรับอักษรภาษาไทยใช้ยูนิโด้ดยาว ๓ ตัวอักษร
ถ้าเราลองพิมพ์

'abcedกขคงจ'

ลงในเชลโต้ตอบโดยที่ไม่ใส่คำสั่ง print ก็จะได้เห็นรูปโฉมที่แท้จริงของข้อความที่ถูกเก็บ นั่นคือ

'abced\xe0\xb8\x81\xe0\xb8\x82\xe0\xb8\x84\xe0\xb8\x87\xe0\xb8\x88'

จะเห็นว่าอักษร abcde ถูกเก็บตามที่พิมพ์ไป แต่อักษรไทยจะถูกเปลี่ยนเป็นโค้ด เช่น "ก" กลายเป็น \xe0\xb8\x81
โดยที่ \x แล้วตามด้วยโค้ด 2 ตัวจะแทนอักษร ๑ ไบต์ ถือว่าเป็น ๑ ตัว ดังนั้นในที่นี้ถือเป็น ๓ ตัวอักษร ซึ่ง ๓ อักษรนี้จะถูกนำไปถอดรหัส utf-8 เป็นอักษร "ก" อีกที

นี่เป็นความเหลื่อมล้ำระหว่างอักษรที่เป็น ASCII กับไม่ใช่ ASCII ซึ่งเกิดขึ้นในไพธอน 2

อย่างไรก็ตามไพธอน 2 สามารถเลือกเก็บสายอักขระเป็นรูปแบบยูนิโค้ดได้ โดยพิมพ์ u ลงไปหน้าเครื่องหมายคำพูด เช่น

u'abcedกขคงจ'

ลองวัดความยาวดู

print(len(u'abcedกขคงจ'))

ผล ที่ได้ก็จะได้ 10 ตามที่ควรจะเป็น เพราะชนิดของข้อมูลกลายเป็นยูนิโค้ดไปทั้งหมดแล้ว ข้อมูลแบบยูนิโค้ดเมื่อนำมาหาความยาวจะถือว่าหนึ่งอักษรคือหนึ่งตัวเท่า เทียมกันหมด

หากลองถามหาชนิดของข้อมูลก็จะพบว่าที่มี u กับไม่มี u ได้ต่างชนิดกัน

print(type('abcedกขคงจ'))
print(type(u'abcedกขคงจ'))

ผลที่ได้คือ

<type 'str'>
<type 'unicode'>

โดย str ในที่นี้หมายถึงสายอักขระชนิด ASCII และ unicode หมายถึงสายอักขระชนิดยูนิโค้ด

แต่สำหรับในไพธอน 3 นั้นสายอักขระธรรมดาก็ถูกเก็บในรูปยูนิโค้ด โดยที่ไม่จำเป็นต้องพิมพ์ u นำหน้า ดังนั้นถ้าพิมพ์แบบนี้ในไพธอน 3 ผลที่ได้คือ

<class 'str'>
<class 'str'>

จะเห็นว่าได้เป็น str ทั้งคู่ แต่ว่า str ในที่นี้ไม่ได้หมายถึงสายอักขระชนิด ASCII แบบในไพธอน 2 แต่หมายถึงสายอักขระชนิดยูนิโค้ดอยู่แล้ว

และข้อมูลชนิด 'unicode' ก็จะไม่มีอีกแล้ว เพราะตัว 'str' นั่นเองที่กลายเป็นยูนิโค้ดไป

ดังนั้นการใส่ u หน้าเครื่องหมายคำพูดในไพธอน 3 นั้นไม่ได้มีความหมายอะไรแล้ว ที่จริงในเวอร์ชัน 3.0 ถึง 3.2 จะไม่สามารถใส่ u นำหน้าได้ แต่ตั้งแต่ 3.3 เป็นต้นมา u ถูกนำกลับมาใหม่ สามารถใส่ได้แม้ว่าจะไม่ได้มีความหมายอะไรแล้วก็ตาม



ปกติเวลาที่นำสายอักขระธรรมดามารวมกับสายอักขระที่เป็นยูนิโค้ดในไพธอน 2 สายอักขระธรรมดาซึ่งเป็น ASCII จะต้องทำการแปลงเป็นยูนิโค้ดโดยทำการถอดรหัส พอรวมแล้วก็จะได้ผลเป็นยูนิโค้ด แต่ถ้าหากในนั้นมีอักษรที่ไม่ใช่ ASCII ปนอยู่จะเกิดขัดข้องขึ้นมา เช่น

print(u'a'+'á')
ได้
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

แต่ u'á'+'a' จะรวมกันได้ไม่มีปัญหา เพราะตัวอักษรที่ไม่ใช่ ASCII นั้นอยู่ในสายอักขระที่เป็นยูนิโค้ดอยู่แล้ว



นอกจากนี้เมธอดบางตัว เช่น upper กับ lower ซึ่งใช้ในการเปลี่ยนตัวอักษรเป็นพิมพ์ใหญ่และพิมพ์เล็กยังทำงานแตกต่างกันด้วย

ในไพธอน 2 สายอักขระโดยทั่วไปอักษรที่นอกเหนือจาก ASCII จะไม่ได้รับผลของ upper กับ lower
ตัวอย่าง

print('hämähäkki'.upper())
ได้
HäMäHäKKI

ซึ่งจะเห็นว่าตัว ä ไม่มีการเปลี่ยนแปลง เพราะไม่ใช่อักษร ASCII

แต่ถ้าเปลี่ยนเป็นยูนิโค้ดโดยเติม u

print(u'hämähäkki'.upper()
ก็จะสามารถเปลี่ยนได้
HÄMÄHÄKKI

นอกจากนี้ในไพธอน 2 เวลาที่เขียนอักษรที่ไม่ใช่ ASCII ลงในโค้ดจะต้องใส่
# -*- coding: utf-8 -*-
ไว้ที่ด้านบนสุดทุกครั้งเพื่อระบุว่าจะใช้การถอดรหัสแบบ utf-8

แต่ว่าในไพธอน 3 ไม่จำเป็นที่จะต้องใส่เพราะว่าเป็นยูนิโค้ด utf-8 เป็นหลักอยู่แล้ว

อ้างอิง



-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ