+1

3 bước để sửa vấn đề Encoding trong Ruby

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!,.... 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?"

invalidundef 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí