3 bước để sửa vấn đề Encoding trong Ruby
Bài đăng này đã không được cập nhật trong 6 năm
Developer thường rất ít khi để ý tới vấn đề encoding của string
. Chúng ta chỉ nghĩ tới chúng khi encoding gặp vấn đề
- chúng ta nhận được 1 exception
Encoding::InvalidByteSequenceError: "\xFE" on UTF-8
- hoặc 1 ký tự nào đó bị hiển thị sai như thế này chẳng hạn
they’re
Khi gặp vấn đề này chúng ta biết ngay nó là do lỗi encoding nhưng làm thế nào để tìm ra vấn đề và làm thế nào để sửa được nó.
Đầu tiên chúng ta phải hiểu encoding là gì trước đã .
Encoding là gì ?
Ta có thể hiểu encoding
như một mảng các bytes hoặc các con số:
irb(main):001:0> "hello!".bytes
=> [104, 101, 108, 108, 111, 33]
Ở ví dụ này thì 104
có thể hiểu là h
, 33
là !
,....
Và nó sẽ phức tạp hơn khi các ký tự của string ít phổ biến hơn trong tiếng Anh:
irb(main):002:0> "hellṏ!".bytes
=> [104, 101, 108, 108, 225, 185, 143, 33]
Ký tự ṏ
thay vì được biểu diễn bởi 1 byte thì nó được biểu diễn bởi 1 nhóm các bytes [225, 185, 143]
, nhưng nó vẫn thể hiện được mối quan hệ giữa bytes và các ký tự.
Hay nói cách khác Encoding
một string chính là xác định mối quan hệ đó.
Để kiểm tra việc này, chúng ta hãy thử với các kiểu encoding khác nhau xem sao:
# Try an ISO-8859-1 string with a special character!
irb(main):003:0> str = "hellÔ!".encode("ISO-8859-1"); str.encode("UTF-8")
=> "hellÔ!"
irb(main):004:0> str.bytes
=> [104, 101, 108, 108, 212, 33]
# What would that string look like interpreted as ISO-8859-5 instead?
irb(main):005:0> str.force_encoding("ISO-8859-5"); str.encode("UTF-8")
=> "hellд!"
irb(main):006:0> str.bytes
=> [104, 101, 108, 108, 212, 33]
Các vấn đề cần lưu ý khi encoding
1. Các byte không thay đổi nhưng các string được hiển thị thì lại khác nhau ?
=> Nhìn vào ví dụ ở trên, đây có lẽ là điều chúng ta phải thật lưu ý, vì việc đổi encoding có thể làm thay đổi string nhưng không thay đổi bytes, do đó ta không nên quá tin vào bytes
.
2. Không phải tất cả string đều được biểu diễn với tất cả encoding
Do hầu hết các encoding đều nhỏ, và không thể xử lý với mọi loại ký tự, nên bạn sẽ gặp phải lỗi nếu một ký tự thuộc encoding nào đó sẽ không tồn tại trong encoding khác hoặc khi Ruby không thể tìm ra cách để chuyển đổi giữa hai bảng mã.
irb(main):006:0> "hi∑".encode("Windows-1252")
Encoding::UndefinedConversionError: U+2211 to WINDOWS-1252 in conversion from UTF-8 to WINDOWS-1252
from (irb):61:in `encode'
from (irb):61
from /usr/local/bin/irb:11:in `<main>'
3. Tuy nhiên với một số options mở rộng của encode
, chúng ta có thể bỏ qua được exception này
irb(main):064:0> "hi∑".encode("Windows-1252", invalid: :replace, undef: :replace)
=> "hi?"
invalid
và undef
sẽ thay thế các ký tự không thể chuyển đổi thành một ký tự khác. Mặc định nó sẽ được đổi thành ký tự ?
(khi convert thành Unicode thì nó thành �
)
Việc thay thế ký tự có thể khiến chúng ta mất thông tin, tuy nhiên thật không may chúng ta không còn cách nào khác, ít ra việc mất 1 ít dữ liệu còn hơn là không thể convert.
3 phương pháp chính giúp chúng ta hiểu encoding:
1. encode Phương pháp này chuyển một string sang một encoding khác, tức là chuyển đổi các ký tự thành các ký tự tương đương trong encoding mới. 2. bytes Phương pháp này sẽ giúp ta nhìn được bytes tạo lên string 3. force_encoding Phương pháp này sẽ cho ta thấy hiển thị khác của cùng 1 byte đó trong encoding khác.
Lưu ý: method encode
khác với force_encoding
ở chỗ encode
thì phải thay đổi bytes
còn force_encoding
thì không.
3 bước xử lý vấn đề encoding
1. Tìm encoding thật sự cho string
của bạn.
Điều này thực sự không đơn giản chút nào. Hãy xem ví dụ sau:
irb(main):078:0> "hi\x99!".encoding
=> #<Encoding:UTF-8>
string
trên thực sự không phải UTF-8
, vì nó có chứa ký tự \x99
, vậy làm thế nào xác định được encoding thật sự ?
Rất nhiều phần mềm cũ sẽ sử dụng encoding mặc định duy nhất, vì vậy bạn có thể tìm hiểu đầu vào từ đây.
Ví dụ: bạn copy string từ Word
, nó có thể là Windows-1252, hoặc một nội dung lấy về từ các website cũ, nó có thể là ISO-8859-1 .
Hoặc có một cách khác, đó là tìm kiểm trên các bảng mã encoding (giống như trên Wikipedia vậy). Từ bảng mã encoding này, bạn có thể tìm thấy các ký tự tham chiếu bởi các số không xác định, và xem chúng có ý nghĩa trong ngữ cảnh hay không.
Trong ví dụ trên, trong bảng mã Windows-1252, byte 99
là ký tự “™”
. Nhưng byte 99
lại không tồn tại trong ISO-8859-1 . Vì vậy để “™”`` có ý nghĩa trong trường hợp này, ta có thể giả định đầu vào là
Window-1252` và tiếp tục.
2. Chọn loại encoding mà bạn muốn cho string
.
Điều này thì rất dễ dàng, trừ khi bạn có một lý do đặc biệt, còn không có thể sử dụng encode UTF-8
.
Một số encoding phổ biến khác có thể sử dụng trong Ruby là ASCII-8BIT
. Trong ASCII-8BIT
thì mọi ký tự đều được biểu diễn bởi 1 byte
str.chars.length == str.bytes.length
Vì vậy nếu bạn muốn kiểm soát số lượng byte trong string, thì ASCII-8BIT
là một lựa chọn tốt.
3. Thực hiện encoding string
của bạn từ encoding ở bước 1 thành encoding ở bước 2.
Với encode
, việc này khá đơn giản
Ví dụ chuyển 1 string từ Windows-1252
sang UTF-8
irb(main):088:0> "hi\x99!".encode("UTF-8", "Windows-1252")
=> "hi™!"
Tham khảo
https://www.justinweiss.com/articles/3-steps-to-fix-encoding-problems-in-ruby/
All rights reserved