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.
Zainstalowałem przez heroku labs ruby 1.9.3, ale wciąż mam ten sam błąd: | – klaut
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
Lub 'string = message.encode ('utf-8', 'iso-8859-1')' może być lepiej. – matt