บทความนี้แตกออกมาจาก 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 เป็นหลักอยู่แล้ว
อ้างอิง