2012-09-08 27 views
5

tôi chỉnh sửa ba tệp có cùng nội dung "你" (là you bằng tiếng Anh) trong ba dạng khác nhau - gbk \ utf-8 \ ucs-2 với gedit có tên "ok1, ok2, ok3 ".unicode endian puzzled me

>>> f1 = open('ok1', 'rb').read() 
>>> f2 = open('ok2', 'rb').read() 
>>> f3 = open('ok3', 'rb').read() 
>>> f1 
'\xc4\xe3\n' 
>>> f2 
'\xe4\xbd\xa0\n' 
>>> f3 
'`O\n\x00' 
>>> hex(ord("`")) 
'0x60' 
>>> hex(ord("O")) 
'0x4f' 

trên thực tế f3 là '\ x60 \ x4f', nhưng sản lượng sau nhầm lẫn tôi

>>> '\xe4\xbd\xa0'.decode("utf-8") 
u'\u4f60' 
>>> '\xc4\xe3'.decode("gbk") 
u'\u4f60' 
>>> 

tại sao chỉ có vấn đề về cuối trong UCS-2 (hay nói unicode), không phải trong utf-8, không phải trong gbk?

Trả lời

5

UTF-8GBK lưu trữ dữ liệu theo chuỗi byte. Nó được xác định mạnh mẽ mà giá trị byte đến sau đó trong các mã hóa này. Thứ tự byte này không thay đổi với kiến ​​trúc được sử dụng trong mã hóa, truyền hoặc giải mã.

Mặt khác, UCS-2 hoặc UTF-16 lưu trữ dữ liệu mới trong chuỗi 2 byte. Thứ tự của các byte riêng lẻ trong các mã thông báo 2 byte này là endianness và nó phụ thuộc vào kiến ​​trúc máy cơ bản. Hệ thống phải có thỏa thuận về cách xác định độ dài của mã thông báo trước khi liên lạc với dữ liệu được mã hóa trong UCS-2.

Trong trường hợp của bạn, điểm Unicode U + 4F60 được mã hóa trong UCS-2 dưới dạng mã thông báo 2 byte duy nhất 0x4F60. Vì máy của bạn đặt byte ít quan trọng nhất trước byte quan trọng nhất trong sắp xếp bộ nhớ, chuỗi ('0x60', '0x4F') đã được đưa vào tệp. Vì vậy, tập tin đọc sẽ mang lại các byte theo thứ tự này.

Python vẫn có thể giải mã dữ liệu này một cách chính xác vì nó sẽ đọc các byte theo đúng thứ tự trước khi hình thành token 2-byte:

>>> '`O\n\x00'.decode('utf-16') 
u'\u4f60\n' 
+0

Vì máy của bạn đặt byte ít quan trọng nhất trước byte quan trọng nhất trong liên kết bộ nhớ, chuỗi ('0x60', '0x4F') đã được đưa vào tệp. Vì vậy, tập tin đọc sẽ mang lại các byte theo thứ tự này. Trong máy của tôi, f1 không phải là '\ xe3 \ xc4 \ n' ?? f2 không phải là f2 '\ xbd \ xe4 \ xa0 \ n' –

+0

@Dd Pp: Vì khi viết một tệp utf-8, gedit đặt byte * một-by-one *. Tuy nhiên, khi viết một tệp mã hóa ucs-2, gedit đặt các byte * hai-by-hai *. Thứ tự trong các byte phụ thuộc vào endianness chỉ trong trường hợp sau. –

3

Endian-Ness chỉ áp dụng cho từ đa byte, nhưng UTF-8 sử dụng đơn vị 8 bit để mã hóa thông tin (đó là những gì 8 trong tên viết tắt). Không bao giờ có câu hỏi về sự nhầm lẫn về trật tự ở đó.

Đôi khi có thể cần nhiều hơn một trong các đơn vị đó để mã hóa thông tin, nhưng chúng được coi là khác biệt. Ví dụ, chữ cái A là một byte, 0x41. Khi nó phải mã hóa một ký tự có nhiều byte hơn, nó sử dụng một byte chỉ báo hàng đầu, tiếp theo là các byte tiếp tục bổ sung để nắm bắt tất cả thông tin cần thiết cho ký tự đó. Một cách hợp lý, đây là những đơn vị riêng biệt.

GBK sử dụng một sơ đồ tương tự; ký tự sử dụng đơn vị 1 byte và giống như UTF-8, một byte thứ hai có thể được sử dụng cho một số ký tự. Mặt khác,

Mặt khác UCS-2 (và kiểu kế thừa là UTF-16) là định dạng 2 byte. Nó mã hóa thông tin theo đơn vị 16 bit, và 16 bit đó luôn đi cùng nhau. 2 byte trong đơn vị đó thuộc về nhau một cách hợp lý, và các kiến ​​trúc hiện đại coi chúng là một đơn vị, và do đó đã đưa ra quyết định theo thứ tự chúng được lưu trữ. Đó là nơi mà endianess xuất hiện, thứ tự của 2 byte trong một đơn vị phụ thuộc vào kiến ​​trúc. Trong kiến ​​trúc của bạn, các byte được sắp xếp bằng cách sử dụng ít kết thúc, có nghĩa là byte 'nhỏ hơn' đi trước. Đây là lý do tại sao byte 0x4F xuất hiện trước byte 0x60 trong tệp của bạn.

Lưu ý rằng trăn có thể đọc lớn hoặc nhỏ endian UTF-16 tốt; bạn có thể chọn endianess một cách rõ ràng nếu không có nhân vật chỉ thị vào lúc bắt đầu (các Byte Order Mark, hoặc BOM):

>>> '`O\n\x00'.decode('utf-16') 
u'\u4f60\n' 
>>> '`O\n\x00'.decode('utf-16-le') 
u'\u4f60\n' 
>>> 'O`\x00\n'.decode('utf-16-be') 
u'\u4f60\n' 

Trong ví dụ sau các byte đã được đảo ngược, và giải mã như lớn về cuối nhỏ.

Các vấn đề liên quan