2013-05-16 23 views
7

Patrząc na źródło Ruby's Base64.encode Nie mogę określić, który kodowanie znaków jest konwertowane na, jeśli w ogóle, przed zakodowaniem tych danych w Base64. Łańcuch Utf-8 zakodowany w Base64 będzie dużo inny niż łańcuch Utf-16 zakodowany w Base64. Czy Ruby składa jakieś obietnice dotyczące tej operacji?Kodowanie znaków w języku Ruby przy użyciu Base64.encode

Odpowiedz

5

fine manual ma do powiedzenia:

encode64 (bin)
Zwraca Base64- zakodowana wersja bin. Metoda ta jest zgodna z RFC 2045.

sekcja 6.8 RFC 2045 mówi:

6.8. Base64 Content-Transfer-Kodowanie

Kodowanie zawartości Base64 służy do reprezentowania dowolnych sekwencji oktetów w postaci, która nie musi być czytelna dla człowieka. [...]

Używany jest 65-literowy podzbiór US-ASCII, umożliwiający reprezentację 6 bitów na jedną drukowaną literę. (Dodatkowa 65-ci znak „=” jest używany do oznaczenia specjalną funkcję przetwarzania.)

Więc Base64 koduje bajtów w ASCII. Jeśli te bajty faktycznie reprezentują łańcuch zakodowany w UTF-8, to ciąg znaków UTF-8 zostanie podzielony na poszczególne bajty, a bajty te zostaną przekonwertowane na Base64; na przykład, jeśli masz ciąg znaków UTF-8 'µ', to skończy się kodowanie bajtów 0xc2 i 0xb5 (w tej kolejności) do reprezentacji Base64 "wrU=\n". Jeśli zaczynasz od binarnego ciągu znaków "\xc2\xb5" (który akurat pasuje do wersji UTF-8 z 'µ'), otrzymasz ten sam wynik "wrU=\n".

Po dekodowaniu "wrU=\n", otrzymasz bajty "\xc2\xb5" i będziesz musiał wiedzieć, że te bajty mają być kodowane w UTF-8, a nie jakaś dowolna kropla bitów. Właśnie dlatego masz oddzielny typ zawartości i zestaw metadanych zestawu znaków dołączony do Base64.

Podobnie, jeśli masz ciąg znaków UTF-16, to zostanie on podzielony na bajty, a bajty te zostaną zakodowane tak jak każdy inny ciąg bajtów. Oczywiście ta sprawa jest nieco bardziej skomplikowana z powodu problemów z kolejnością bajtów, ale właśnie dlatego mamy nagłówki typów treści i zestawów znaków oraz LM.

Najważniejsze jest to, że Base64 współpracuje z bajtami, a nie znakami. Jaki format (tekst w formacie UTF-8, tekst w formacie UTF-16, obraz PNG, ...) jest problemem kogoś innego. Base64 po prostu konwertuje strumień bajtowy na podzbiór US ASCII, a następnie z powrotem do bajtów; format tych bajtów musi być określony osobno.


Zrobiłem trochę szturchania w źródle, a wyniki mogą być interesujące, nawet jeśli nie są całkowicie odpowiednie.encode64 method jest po prostu tak:

def encode64(bin) 
    [bin].pack("m") 
end 

Następnie, jeśli spojrzeć przez Array#pack:

static VALUE 
pack_pack(VALUE ary, VALUE fmt) 
{ 
    /*...*/ 
    int enc_info = 1;  /* 0 - BINARY, 1 - US-ASCII, 2 - UTF-8 */ 

i pilnować enc_info, zobaczysz, że Formatowanie 'm' pozostawi enc_info sam tak zapakowanym łańcucha wyjdzie jako US-ASCII, a więc encode64 będzie wytwarzać dane wyjściowe ASCII w USA zgodnie z oczekiwaniami.

+0

Zgadzam się z moimi podejrzeniami w komentarzach odpowiedzi Wiktora. Dziękuję za potwierdzenie. – Brent

19

Przykładem do kodowania i dekodowania ciąg UTF-8 w base64:

text = "intérnalionálização" 
=> "intérnalionálização" 
text.encoding 
=> #<Encoding:UTF-8> 
encoded = Base64.encode64(text) 
=> "aW50w6lybmFsaW9uw6FsaXphw6fDo28=\n" 
encoded.encoding 
=> #<Encoding:US-ASCII> 
decoded = Base64.decode64(encode) 
=> "int\xC3\xA9rnalion\xC3\xA1liza\xC3\xA7\xC3\xA3o" 
decoded.encoding 
=> #<Encoding:US-ASCII> 
decoded = decoded.force_encoding('UTF-8') 
=> "intérnalionálização" 
decoded.encoding 
=> #<Encoding:UTF-8> 
+0

Interesujące. Ciąg zwrócony z dekodowania64 to US-ASCII i zawiera garść znaków zbiegów. Przypuszczam, że jest to całkiem niezły wskaźnik, że konwertuje go na US-ASCII, zanim go również zakoduje kod base64. – Brent

+1

Aby zrozumieć: http://api.rubyonrails.org/classes/Base64.html i http://apidock.com/ruby/Array/pack –

+1

Jeśli chcesz wypróbować kod Victora w sesji irbowej, upewnij się, że Najpierw musisz "wymagać" bazy64. – Gokul