Tôi vừa mới cập nhật từ ruby 1.9.2 lên ruby 1.9.3p0 (2011-10-30 bản sửa đổi 33570). Ứng dụng đường ray của tôi sử dụng postgresql làm phần cuối cơ sở dữ liệu của nó. Ngôn ngữ hệ thống là UTF8, cũng như mã hóa cơ sở dữ liệu. Mã hóa mặc định của ứng dụng đường ray cũng là UTF8. Tôi có người dùng Trung Quốc nhập các ký tự Trung Quốc cũng như các ký tự tiếng Anh. Các chuỗi được lưu trữ dưới dạng chuỗi được mã hóa UTF8.Rails: mã hóa tai ương với băm serialized mặc dù UTF8
Phiên bản đường ray: 3.0.9
Vì cập nhật một số chuỗi Trung Quốc hiện có trong cơ sở dữ liệu không còn hiển thị chính xác nữa. Điều này không ảnh hưởng đến tất cả các chuỗi, nhưng chỉ ảnh hưởng đến tất cả các chuỗi ký tự. Tất cả các chuỗi khác được lưu trữ dưới dạng các chuỗi đơn giản vẫn có vẻ đúng.
Ví dụ:
Đây là một hash serialized được lưu trữ như là một chuỗi UTF8 trong cơ sở dữ liệu:
broken = "--- !map:ActiveSupport::HashWithIndifferentAccess \ncheckbox: \"1\"\nchoice: \"Round Paper Clips \\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n\"\ninfo: \"10\\xE7\\x9B\\x92\"\n"
Để chuyển đổi chuỗi này để băm ruby, tôi deserialize nó với YAML.load
:
broken_hash = YAML.load(broken)
Điều này trả về một hàm băm có nội dung bị cắt xén:
{"checkbox"=>"1", "choice"=>"Round Paper Clips ï¼\u0088å\u009B\u009Eå½¢é\u0092\u0088ï¼\u0089\r\n", "info"=>"10ç\u009B\u0092"}
Công cụ bị cắt xén được coi là tiếng Trung Quốc được mã hóa UTF8. broken_hash['info'].encoding
cho tôi biết rằng ruby nghĩ rằng đây là #<Encoding:UTF-8>
. Tôi không đồng ý.
Điều thú vị là, tất cả các chuỗi khác không được tuần tự hóa trước khi được xem là tốt. Trong cùng một bản ghi, một trường khác chứa các ký tự Trung Quốc trông vừa phải --- trong bảng điều khiển đường ray, giao diện điều khiển psql và trình duyệt. Mỗi chuỗi --- không có vấn đề nếu băm serialized hoặc chuỗi đồng bằng --- lưu vào cơ sở dữ liệu kể từ khi cập nhật cũng tốt.
tôi đã cố gắng để chuyển đổi văn bản bị cắt xén từ một mã hóa sai có thể (như GB2312 hoặc ANSI) sang UTF-8 bất chấp tuyên bố của ruby rằng đây đã là UTF-8 và dĩ nhiên là tôi đã thất bại. Đây là mã tôi đã sử dụng:
require 'iconv'
Iconv.conv('UTF-8', 'GB2312', broken_hash['info'])
Điều này không thành công vì Ruby không biết phải làm gì với chuỗi bất hợp pháp trong chuỗi.
Tôi thực sự chỉ muốn chạy tập lệnh để sửa tất cả các chuỗi băm được mã hóa cũ, có lẽ bị hỏng và được thực hiện với nó. Có cách nào để chuyển đổi những sợi dây bị hỏng này thành một thứ gì đó giống với Trung Quốc một lần nữa không?
Tôi vừa chơi với chuỗi UTF-8 được mã hóa trong chuỗi thô (được gọi là "bị hỏng" trong ví dụ trên). Đây là chuỗi Trung Quốc mà được mã hóa trong chuỗi tuần tự:
chinese = "\\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n\"
tôi nhận thấy rằng nó rất dễ dàng để chuyển đổi này cho một người thực UTF-8 chuỗi mã hóa bằng cách tự thoát nó (loại bỏ các dấu xồ nguợc thoát).
chinese_ok = "\xEF\xBC\x88\xE5\x9B\x9E\xE5\xBD\xA2\xE9\x92\x88\xEF\xBC\x89\r\n"
này trả về một chuỗi thích hợp Trung Quốc UTF-8 mã hóa: "(回形针)\r\n"
Điều sụp đổ chỉ khi tôi sử dụng YAML.load(...)
để chuyển đổi chuỗi thành một hash ruby. Có lẽ tôi nên xử lý chuỗi thô trước khi nó được cấp cho YAML.load
. Chỉ cần làm cho tôi tự hỏi tại sao điều này lại như vậy ...
Thú vị! Điều này có thể là do động cơ YAML "psych" được sử dụng theo mặc định trong phiên bản 1.9.3. Tôi chuyển sang công cụ "syck" với YAML::ENGINE.yamler = 'syck'
và các chuỗi bị hỏng được phân tích cú pháp chính xác.
Loại cột cho băm được in ra? –
@muistooshort: loại cột là 'văn bản'. – rekado
Điều gì xảy ra nếu bạn thay đổi cột thành 'binary'? Điều đó sẽ nhận được chuỗi như "8bit ASCII" (tức là byte nguyên) và có thể sẽ khởi động 'YAML.load' thành hình dạng. Như một bài kiểm tra nhanh, bạn có thể 'broken.force_encoding ('binary')' trước 'YAML.load (broken)'. –