2012-04-06 31 views
11

Tôi có một hàng đợi tin nhắn văn bản trong Redis. Giả sử một thông báo trong redis là một cái gì đó như thế này:Rails, Heroku và chuỗi byte không hợp lệ trong lỗi UTF-8

"niño" 

(phát hiện ký tự không chuẩn).

Ứng dụng đường ray hiển thị hàng đợi tin nhắn. Khi tôi kiểm tra cục bộ (Rails 3.2.2, Ruby 1.9.3) mọi thứ đều ổn, nhưng trên cây tuyết tùng Heroku (Rails 3.2.2, tôi tin rằng có ruby ​​1.9.2) Tôi nhận được lỗi khét tiếng: ActionView::Template::Error (invalid byte sequence in UTF-8)

Sau đọc và đọc lại tất cả những gì tôi có thể tìm thấy trực tuyến Tôi vẫn bị mắc kẹt như thế nào để sửa lỗi này.

Bất kỳ trợ giúp hoặc điểm nào để đi đúng hướng đều được đánh giá cao!

chỉnh sửa:

Tôi đã tìm được giải pháp. Tôi đã kết thúc bằng cách sử dụng Iconv:

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

Không có câu trả lời được đề xuất nào tôi tìm thấy có vẻ hoạt động trong trường hợp của tôi.

+0

tôi đã cài đặt thông qua phòng thí nghiệm heroku ruby ​​1.9.3, nhưng tôi vẫn gặp lỗi tương tự: | – klaut

+2

Khi yêu cầu Iconv trong Ruby 1.9.3 bạn nhận được cảnh báo này: 'iconv sẽ không được dùng nữa trong tương lai, hãy sử dụng String # encode thay thế.' Tương đương với giải pháp của bạn sẽ là:' string.force_encoding ('iso-8859- Mã hóa '') '(' utf-8 ') '. – matt

+2

Hoặc 'string = message.encode ('utf-8', 'iso-8859-1')' có thể tốt hơn. – matt

Trả lời

38

On Heroku, khi ứng dụng của bạn nhận được thông báo "Nino" từ Redis, nó thực sự nhận được bốn byte:

0x6e 0x69 0xf1 0x6f 

mà khi hiểu là ISO-8859-1 tương ứng với các ký tự n, i, ño.

Tuy nhiên, ứng dụng Rails của bạn giả định rằng các byte này nên được hiểu là UTF-8 và tại một số thời điểm, nó cố gắng giải mã chúng theo cách này. Byte thứ ba trong chuỗi này, 0xf1 trông như thế này:

1 1 1 1 0 0 0 1 

Nếu bạn so sánh này cho table on the Wikipedia page, bạn có thể nhìn thấy byte này là byte hàng đầu của một nhân vật Bốn byte (nó phù hợp với mô hình 11110xxx), và như vậy nên được theo sau bởi ba byte tiếp tục khác mà tất cả đều khớp với mẫu 10xxxxxx. Nó không phải, thay vì byte tiếp theo là 0x6f (01101111), và do đó, đây là chuỗi byte utf-8 không hợp lệ và bạn nhận được lỗi mà bạn nhìn thấy.

Sử dụng:

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

(hoặc Iconv tương đương) cho Ruby để đọc message như ISO-8859-1 mã hóa, và sau đó để tạo ra các chuỗi tương đương trong mã UTF-8, mà sau đó bạn có thể sử dụng không vấn đe. (Một giải pháp thay thế có thể là sử dụng force_encoding để cho Ruby biết mã hóa chính xác của chuỗi, nhưng điều đó có thể sẽ gây ra sự cố sau này khi bạn cố gắng trộn các chuỗi UTF-8 và ISO-8859-1).

Trong UTF-8, chuỗi "Nino" tương ứng với các byte:

0x6e 0x69 0xc3 0xb1 0x6f 

Lưu ý rằng các byte đầu tiên, thứ hai và cuối cùng đều giống nhau. Ký tự ñ được mã hóa dưới dạng hai byte 0xc3 0xb1.Nếu bạn viết chúng ra dưới dạng nhị phân và so sánh với bảng trong bài viết Wikipedia, bạn sẽ thấy chúng mã hóa 0xf1, mã hóa ISO-8859-1 là ñ (vì 256 điểm mã unicode đầu tiên khớp với ISO-8859-1) .

Nếu bạn có những lăm byte và đối xử với họ như là tiêu chuẩn ISO-8859-1, sau đó chúng tương ứng với chuỗi

niño 

Nhìn vào ISO-8859-1 codepage, 0xc3 bản đồ để Â và bản đồ 0xb1 để ±.

Vì vậy, những gì đang xảy ra trên máy cục bộ của bạn là ứng dụng của bạn đang nhận năm byte 0x6e 0x69 0xc3 0xb1 0x6f từ Redis, là biểu diễn UTF-8 của "niño". Trên Heroku nó nhận được bốn byte 0x6e 0x69 0xf1 0x6f, đại diện ISO-8859-1.

Sửa lỗi thực sự cho vấn đề của bạn sẽ là đảm bảo các chuỗi được đưa vào Redis đều đã là UTF-8 (hoặc ít nhất là tất cả cùng một mã hóa). Tôi đã không sử dụng Redis, nhưng từ những gì tôi có thể nói từ một Google ngắn gọn, nó không quan tâm chính nó với mã hóa chuỗi nhưng chỉ đơn giản là trả lại bất kỳ byte nó đã được đưa ra. Bạn nên xem xét bất kỳ quy trình nào đang đưa dữ liệu vào Redis và đảm bảo rằng nó xử lý mã hóa đúng cách.

+0

Rất kỹ lưỡng. +1 – coreyward

+0

câu trả lời rất hay, cảm ơn! – klaut

+2

wow, đây là câu trả lời như thế nào! – Cristian

Các vấn đề liên quan