2012-09-08 15 views
5

ho modificato tre file con lo stesso contenuto "你" (è you in inglese) in tre diverse forme: gbk \ utf-8 \ ucs-2 con gedit denominato "ok1, ok2, ok3 ".unicode endian mi ha perplesso

>>> 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' 

infatti F3 è '\ x60 \ x4f', ma il seguente risultato mi ha confuso

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

perché solo non v'è problema endian in UCS-2 (o dire unicode), non in utf-8, non in gbk?

risposta

5

UTF-8 e GBK memorizzare i dati in una sequenza di byte. È fortemente definito quale valore di byte viene dopo di che in queste codifiche. Questo ordine di byte non cambia con l'architettura utilizzata nella codifica, trasmissione o decodifica.

D'altra parte, UCS-2 o nuovi 16 UTF- dati conservare le sequenze di 2-byte. L'ordine dei singoli byte all'interno di questi token a 2 byte è il endianness e dipende dall'architettura della macchina sottostante. I sistemi devono avere un accordo su come identificare la endianità dei token prima di comunicare con i dati codificati in UCS-2.

Nel tuo caso, il punto Unicode U + 4F60 è codificato in UCS-2 come un singolo token a 2 byte 0x4F60. Poiché la tua macchina mette il byte meno significativo prima di quello più significativo nell'allineamento della memoria, la sequenza ('0x60', '0x4F') è stata inserita nel file. Pertanto, il file letto produrrà i byte in questo ordine.

Python può ancora decodificare questi dati correttamente poiché leggerà i byte in ordine corretto prima di formare il token 2-byte:

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

Poiché la macchina mette il byte meno significativo prima di quello più significativo nell'allineamento della memoria, la sequenza ('0x60', '0x4F') è stata inserita nel file. Quindi, il file read restituirà i byte in questo ordine. Perché nella mia macchina, f1 non è '\ xe3 \ xc4 \ n' ?? f2 non è f2 '\ xbd \ xe4 \ xa0 \ n' –

+0

@Dd Pp: Perché quando si scrive un file utf-8, gedit mette byte * uno per uno *. Tuttavia, durante la scrittura di un file codificato ucs-2, gedit inserisce i byte * due per due *. L'ordine entro byte dipende dal endianness solo in quest'ultimo caso. –

3

Endian-ità applica solo a parole multi-byte, ma UTF-8 usa unità di 8 bit per codificare le informazioni (questo è ciò che rappresenta l'8 nel nome). Non c'è mai la questione della confusione nell'ordinare lì.

A volte può essere necessario più di una di quelle unità per codificare le informazioni, ma sono considerate distinte. La lettera A è un byte, 0x41, ad esempio. Quando deve codificare un carattere con più byte, utilizza un byte indicatore iniziale, seguito da byte di continuazione aggiuntivi per acquisire tutte le informazioni necessarie per quel personaggio. Logicamente, queste sono unità distinte.

GBK utilizza uno schema simile; i caratteri usano unità di 1 byte e, proprio come UTF-8, è possibile utilizzare un secondo byte per alcuni caratteri.

UCS-2 (e il suo successore, UTF-16), d'altra parte, è un formato a 2 byte. Codifica le informazioni in unità di 16 bit e quei 16 bit vanno sempre insieme. I 2 byte in quell'unità appartengono insieme logicamente, e le architetture moderne trattano questi come un'unità, e quindi hanno preso una decisione in quale ordine sono memorizzati. È qui che entra in gioco l'endianess, l'ordine dei 2 byte in un'unità dipende dall'architettura. In l'architettura, i byte vengono ordinati utilizzando little-endianess, il che significa che il byte "più piccolo" va prima. Questo è il motivo per cui il byte 0x4F arriva prima del byte 0x60 nel file.

Si noti che python può leggere bene o molto bene l'endian UTF-16; È possibile scegliere l'endianess esplicitamente se non v'è alcun carattere indicatore alla partenza (Byte Order Mark, o 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' 

In quest'ultimo esempio, i byte sono stati invertiti, e decodificato come big-endian.

Problemi correlati