2012-04-06 16 views
11

Mam kolejkę wiadomości tekstowych w Redis. Powiedzmy, że komunikat na czerwono jest mniej więcej taki:Szyny, Heroku i niepoprawna sekwencja bajtów w błędzie UTF-8

"niño" 

(zobacz niestandardowy znak).

Aplikacja szyn wyświetla kolejkę komunikatów. Kiedy testuję lokalnie (Rails 3.2.2, Ruby 1.9.3) wszystko jest w porządku, ale na Heroku cedarze (Rails 3.2.2, wierzę, że jest rubin 1.9.2) dostaję niesławny błąd: ActionView::Template::Error (invalid byte sequence in UTF-8)

Po czytając i ponownie czytając wszystko, co mogłem znaleźć w Internecie, wciąż utknąłem, jak to naprawić.

Każda pomoc lub punkt we właściwym kierunku jest bardzo doceniana!

edycja:

Udało mi się znaleźć rozwiązanie. I skończył przy użyciu iconv:

string = Iconv.iconv('UTF-8', 'ISO-8859-1', message)[0] 

Żaden z sugerowanych odpowiedzi znalazłem wokół wydają się działać w moim przypadku.

+0

Zainstalowałem przez heroku labs ruby ​​1.9.3, ale wciąż mam ten sam błąd: | – klaut

+2

Gdy wymaga się Iconv w Rubim 1.9.3, otrzymasz następujące ostrzeżenie: 'iconv będzie przestarzałe w przyszłości, zamiast tego użyj kodowania String # Odpowiednikiem twojego rozwiązania będzie coś takiego:' string.force_encoding ('iso-8859- 1 '). Encode (' utf-8 ') '. – matt

+2

Lub 'string = message.encode ('utf-8', 'iso-8859-1')' może być lepiej. – matt

Odpowiedz

38

Na Heroku, gdy aplikacja odbiera komunikat "Niño" z Redis, to jest rzeczywiście coraz cztery bajty:

0x6e 0x69 0xf1 0x6f 

które po interpretowane jako ISO-8859-1 odpowiadające znakom n, i, ñ i o.

Jednak Twoja aplikacja Railsowa zakłada, że ​​bajty te powinny być interpretowane jako UTF-8 iw pewnym momencie próbuje je odkodować w ten sposób. Trzeci bajt w tej sekwencji, 0xf1 wygląda następująco:

1 1 1 1 0 0 0 1 

Jeśli porównać to do table on the Wikipedia page można zobaczyć ten bajt jest wiodącym bajt czterech bajtów charakter (jest zgodny ze wzorcem 11110xxx) oraz jako takie powinny następować trzy kolejne bajty kontynuacyjne, które wszystkie pasują do wzorca 10xxxxxx. Nie jest, zamiast tego następny bajt to 0x6f (01101111), a więc jest to nieprawidłowa sekwencja bajtów utf-8 i pojawia się błąd, który widzisz.

Zastosowanie:

string = message.encode('utf-8', 'iso-8859-1') 

(lub równowartość Iconv) mówi Ruby czytać message jako ISO-8859-1 zakodowany, a następnie stworzyć odpowiednik ciąg w kodowaniu UTF-8, które można następnie użyć bez problemów. (Alternatywą może być użycie force_encoding, aby poinformować Ruby o poprawnym kodowaniu ciągu znaków, ale prawdopodobnie spowoduje to problemy później, gdy spróbujesz połączyć struny UTF-8 i ISO-8859-1).

w UTF-8, ciąg "Niño" odpowiada bajtów:

0x6e 0x69 0xc3 0xb1 0x6f 

Zauważ, że pierwszy, drugi i ostatnie bajty są takie same. Znak ñ jest kodowany jako dwa bajty 0xc3 0xb1.Jeśli napiszesz je w formacie binarnym i porównasz do tabeli w artykule w Wikipedii, zobaczysz, że kodują one kod 0xf1, który jest kodowaniem ISO-8859-1 z ñ (ponieważ pierwsze 256 punktów kodowych unicode pasuje do ISO-8859-1) .

Jeśli wziąć te pięć bajtów i traktować je jako ISO-8859-1, a następnie odpowiadają one ciąg

niño 

Patrząc na ISO-8859-1 codepage, 0xc3 mapuje do  i 0xb1 mapy do ±.

To, co dzieje się na twoim komputerze lokalnym, to to, że twoja aplikacja otrzymuje pięć bajtów 0x6e 0x69 0xc3 0xb1 0x6f od Redis, która jest reprezentacją UTF-8 "niño". Na Heroku otrzymuje cztery bajty 0x6e 0x69 0xf1 0x6f, czyli reprezentację ISO-8859-1.

Prawdziwą poprawą twojego problemu będzie upewnienie się, że ciągi przesyłane do Redis są już kodowaniem UTF-8 (lub przynajmniej takim samym kodowaniem). Nie używałem Redisa, ale z tego, co mogę powiedzieć od krótkiego Google'a, nie wiąże się ono z kodowaniami łańcuchowymi, ale po prostu zwraca wszystkie bajty, które zostały mu nadane. Powinieneś popatrzeć na proces wprowadzania danych do Redis i upewnić się, że poprawnie obsługuje kodowanie.

+0

Bardzo dokładny. +1 – coreyward

+0

bardzo dobra odpowiedź, dzięki! – klaut

+2

Wow, tak powinny wyglądać odpowiedzi! – Cristian

Powiązane problemy