2012-03-16 16 views
5

In .NET perché non è vero che:Perché non è `Encoding.UTF8.GetBytes (Encoding.UTF8.GetString (x)) == x`

Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(x)) 

restituisce l'array di byte originale per un array di byte arbitrario x?

È mentioned in risposta a un'altra domanda ma il rispondente non spiega il motivo.

+0

La risposta che hai collegato a parla di ASCII, non di UTF-8. – svick

+1

Puoi persino confrontare gli array di byte usando '=='? Questo probabilmente confronta solo i loro riferimenti, probabilmente dovrai fare un ciclo per confrontare ogni elemento dell'array per l'uguaglianza. – Matthew

+0

@Matthew the gist of [that answer] (http://stackoverflow.com/a/3946274/85371) sembra essere che la codifica può variare. E sì, il codice di esempio è imperfetto/arretrato. – sehe

risposta

1

Le codifiche di caratteri (UTF8, in modo specifico) possono avere forme diverse per lo stesso punto di codice.

Così quando si converte in una stringa e indietro, i byte effettivi potrebbero rappresentare una forma diversa (canonica).

Vedi anche String.Normalize(NormalizationForm.System.Text.NormalizationForm.FormD)

Vedi anche:

Alcune sequenze Unicode sono contro equivalente idered perché rappresentano lo stesso personaggio. Ad esempio, i seguenti sono considerati equivalenti perché nessuno di questi può essere usato per rappresentare "A":

"\u1EAF" 
"\u0103\u0301" 
"\u0061\u0306\u0301" 

Tuttavia, ordinale, cioè binario, comparazioni considerare queste sequenze differenti perché contengono diversi valori di codice Unicode. Prima di eseguire confronti ordinali, le applicazioni devono normalizzare queste stringhe per decomporle nei componenti di base.

Quella pagina è dotato di un bel esempio che mostra cosa codifiche sono sempre normalizzate

+0

Perché uno dei due metodi potrebbe cambiare la forma della stringa? – svick

+0

@svick Non chiedermelo. Non ho controllato la documentazione per assicurarmi che non funzionasse, anche se – sehe

+0

penso che questo non accadrà. Questo perché quelle diverse forme sono * non * proprietà delle varie codifiche, ma dello stesso Unicode. Quindi, un personaggio può essere rappresentato come sequenze di codepoint diverse. Ma una singola sequenza di codepoint può essere rappresentata solo in un modo come una sequenza di byte quando si utilizza una codifica specifica. – svick

1

Questo perché == non confrontare ogni elemento dell'array. Non ha alcuna connessione con Encoding.UTF8. Controllare questo:

var a = new byte[] { 1 }; 
var b = new byte[] { 1 }; 
bool res = a == b; 
3

In primo luogo, come accennato watbywbarif, non si dovrebbe confrontare le sequenze utilizzando ==, che non funziona.

Ma anche se si confrontano gli array correttamente (ad esempio utilizzando SequenceEquals() o semplicemente osservandoli), non sono sempre gli stessi. Un caso in cui ciò può verificarsi è se x è una stringa codificata UTF-8 non valida.

Ad esempio, la sequenza di 1 byte di 0xFF non è UTF-8 valida. Quindi, cosa restituisce Encoding.UTF8.GetString(new byte[] { 0xFF })? È , U + FFFD, CARATTERE DI SOSTITUZIONE. E ovviamente, se chiami lo Encoding.UTF8.GetBytes(), non ti restituisce 0xFF.

+0

+1 da me, bel esempio – sehe

+1

Non sapevo del metodo di estensione 'SequenceEqual', molto utile. – PyreneesJim

1

Un altro angolo di venire a questo da è che le classi Encodingsono progettato per i dati di andata e ritorno, ma i dati sono progettati per andata e ritorno è char dati, codificati a byte, non il contrario .Ciò significa che, all'interno delle capacità dello Encoding in questione, ciascun valore char ha una codifica corrispondente nei valori byte (1 o più) che torneranno esattamente nello stesso valore char. (Vale la pena notare che non tutti i Encoding s può fare questo per tutti possibili char valori - per esempio, può solo sostenere char valori nella gamma [0, 128).)

Quindi, se siete di partenza con il carattere dati e hai bisogno di un modo per archiviarli o inviarli in un supporto che funziona con byte (come un file su disco o un flusso di rete), Encoding è un ottimo modo per convertire i dati char in dati byte e poi di nuovo sul altra fine. (Se si desidera supportare tutte le possibili stringhe, è necessario utilizzare uno dei Unicode-based Encoding s, come ad esempio Encoding.Unicode o Encoding.UTF8.)

Allora, che cosa significa questo se si sta iniziando con un mucchio di byte s? Bene, a seconda della codifica in questione, lo byte s con cui stai lavorando potrebbe non essere effettivamente una sequenza che Encoding avrebbe mai prodotto. Hai bisogno di guardare a Encoding.GetBytes come operazione codifica, e Encoding.GetChars/Encoding.GetString come decodifica funzionamento, e così si sta iniziando con una serie arbitraria di byte e cercando di decodificarli.

Per un'analogia, prendere in considerazione il formato di file JPEG per le immagini. Questo ha un tipo simile di codifica e decodifica, dove in questo caso i dati decodificati non sono un string ma un'immagine. Quindi, se prendi una stringa arbitraria di byte, quali sono le possibilità che possa essere decodificata come immagine JPEG? La risposta a questo, ovviamente, è molto sottile. Più probabilmente, i tuoi byte finiranno per scendere un percorso nel decodificatore che dice "Woah lì, non mi aspettavo quel byte per venire dopo quell'altro", e farà del suo meglio per gestire i dati sull'ipotesi che è un file JPEG valido che è stato danneggiato in qualche modo.

Esattamente la stessa cosa accade quando si converte una serie arbitraria di byte in una stringa. La codifica UTF-8 ha regole specifiche su come i codici char 128 vengono codificati, e una di queste regole dice che vedrai sempre un byte che corrisponde allo schema di bit 10xxxxxx dopo uno che corrisponde a uno schema come 110xxxxx, 1110xxxx o 11110xxx, che "introduce" una sequenza multi-byte (più byte s che rappresenta un singolo char). Quindi, se i tuoi dati contengono un byte corrispondente al modello che non corrisponde a seguendo uno degli "introduttori" previsti, l'encoder può solo presumere che i dati siano stati danneggiati in qualche modo. Che cosa fa? Inserisce un personaggio che dice: "Qualcosa è andato terribilmente storto con i dati codificati, ho fatto del mio meglio e qui è andato storto". Le persone che hanno progettato Unicode hanno anticipato questo scenario esatto e creato un personaggio con questo preciso significato: lo Replacement Character.

Quindi, se si sta cercando di andata e ritorno le vostre byte s in una serie di char s e si incontra questo scenario, il valore effettivo della incriminato byte si perde, e invece viene inserito un carattere di rimpiazzo. Quando si prova a trasformare lo string in un array byte, finisce la codifica del Carattere sostitutivo, non i dati originali. I dati originali sono persi.

Quello che stai cercando è una codifica & relazione di decodifica che funziona nella direzione opposta. Encoding consente di acquisire i dati char e di trovare un modo per archiviarli temporaneamente come dati byte. Se si desidera prendere i dati byte e trovare un modo per archiviarli temporaneamente come dati char, è necessaria una codifica progettata per tale scopo specifico. Fortunatamente, questi esistono. Wikipedia ha un fairly comprehensive list delle opzioni. :-)

All'interno di .NET Framework, l'opzione più semplice e più accessibile è la codifica MIME Base-64, che viene esposta tramite Convert.ToBase64String e Convert.FromBase64String.

Problemi correlati