+1

Một số vấn đề encoding trong Ruby và biện pháp khắc phục

1. Mở đầu

Khi lập trình Ruby and Rails, đã bao giờ bạn gặp phải tình huống check log và tìm thấy dòng lỗi:

  Encoding::InvalidByteSequenceError: "\xFE" on UTF-8

hay muốn hiển thị đoạn string "they're" hiện thị thành “they’re”. Khi đó bạn mới quan tâm đến các lỗi về encode trong ruby.

Vậy Encoding là gì?

Trong ruby bạn có thể hình dùng 1 string sẽ là một mảng bytesbytes dạng như sau:

"ruby on rails".bytes
 => [114, 117, 98, 121, 32, 111, 110, 32, 114, 97, 105, 108, 115]

với 114 đại diện cho chữ r, 117 đại diện cho chữ u...

Bây giờ trong trường hợp trong chuỗi string sử dụng một số từ ít dùng trong tiếng anh ví dụ một số từ tiếng Việt như sau: â. ư, ê...: ta sẽ thấy như sau:

"lập trình".bytes
 => [108, 225, 186, 173, 112, 32, 116, 114, 195, 172, 110, 104] 

2.3.3 :006 > "lap trinh".bytes
 => [108, 97, 112, 32, 116, 114, 105, 110, 104] 

Có thể thấy chữ 'ậ' bây giờ được biểu diễn bởi 3 số [225, 186,173"]

Có thể thấy ở đây có một mối quan hệ giữ kí tự , mảng bytes và encoding của một chuỗi .

Có rất nhiều kiểu encoding khác nhau, bây giờ ta thử mã hoá bằng các encoding khác nhau của cùng 1 chuỗi.

2.3.3 :021 > str = "lập trình".force_encoding("ISO-8859-1"); str.encode("UTF-8") 
 => "lập trình" 
2.3.3 :022 > str = "lập trình".force_encoding("ISO-8859-5"); str.encode("UTF-8") 
 => "lсК­p trУЌnh" 

Ta có thể thấy với cùng một chuỗi nhưng với các loại encoding khác nhau thì chuỗi đó hiển thị khác nhau mặc dù biểu diễn bởi bytes kí tự là như nhau

Vì vậy dù ta có đổi encode nào đi chăng nữa mảng bytes cũng ko hề thay đổi Một điểm cần lưu ý nữa ở đây là ko phải chuỗi nào cũng có thể encode được với mọi loại encoding. Ví dụ với chuỗi trên khi ta force_encoding với kiểu encode US-ASCII sẽ báo lỗi ngay"

2.3.3 :023 > str = "lập trình".force_encoding("US-ASCII"); str.encode("UTF-8")
Encoding::InvalidByteSequenceError: "\xE1" on US-ASCII
	from (irb):23:in `encode'
	from (irb):23
	from /Users/mac/.rvm/rubies/ruby-2.3.3/bin/irb:11:in `<main>'

3 methods cần quan tâm đến encoding:

  • encode, chuyển đổi một chuỗi từ kiểu encode này sang kiểu encode khác
  • bytes, hiển thị các bytes của một chuỗi
  • force_encoding, cũng giống như encode là chuyển đổi một chuỗi từ kiểu encode này sang kiểu encode khác. Tuy nhiên điểm khác nhau cơ bản giữa hai phương thức này là encode có thể thay đổi bytes còn force_encoding thì không:
    irb(main):060:0> str = str.force_encoding("UTF-8")
    irb(main):061:0> str.encoding
    => #<Encoding:UTF-8> 

2. Các biện pháp khắc phục

Tìm ra đúng bộ encoding của string:

    irb(main):078:0> "hi\x99!".encoding
    => #<Encoding:UTF-8>

Nghe có vẻ dễ nhưng hãy nhìn ví dụ trên nếu string trên thực sự là UTF-8, nó sẽ ko có kiểu {number} như vậy, Vậy làm sao để tìm ra đúng encoding cho string bạn đang có ?
Cách thứ nhất: Theo kinh nghiệm thì rất nhiều phần mềm cũ sẽ chỉ có một kiểu encoding. Ví dụ: trong Word? có thể là Windows-1252, hay những website cũ thường sử dụng ISO-8859-1. Cách thứ 2 là lục lọi ở danh sách các encoding trên Internet và xem có ký tự nào phụ hợp cho chuỗi số kia không. Từ đó có thể tìm ra encoding của chuổi đó. Ví dụ, trong Window-1252, byte 99 đại diện cho ký tự “™” và nó lại không tồn tại trong ISO-8859. Do đó một giả thuyết hợp lý rằng input nằm trong Window-1252.

Mặt khác, có thể tiếp tục tìm kiếm cho đến khi gặp được ký tự matching hợp lý hơn.

Quyết định encoding sẽ mã hóa string

Hiểu một cách đơn giản, nếu không có lý do gì thực sự đặc biệt, hãy sử dụng UTF-8. Một số trường hợp hiếm gặp, có thể sẽ phải dùng tới ASCII-8BIT trong Ruby. Với bộ mã này, mỗi ký tự được đại diện bởi duy nhất một byte, kể cả những ký tự đặc biệt đi nữa. Do đó, nó sẽ là lựa chọn tốt khi bạn muốn xử lý từng byte một trong string của mình.

Re-encode cho đến khi nào thỏa mãn

    irb(main):088:0> "hi\x99!".encode("UTF-8", "Windows-1252")
    => "hi™!"

Trong ví dụ này, string đang ở Window-1252 nhưng tôi thích nó thành UTF-8 và chúng ta có thể dễ dàng thực hiện điều đó trong encode

Bạn cũng có thể dùng hàm force_encoding để dễ dàng chuyển đổi.

3. Kết luận

Những lỗi trong encoding tưởng là nhỏ có thể fix rất dễ dàng nhưng nếu người lập trình ko để ý sẽ gây ra những lỗi hiển thị rất xấu lên màn hình or lỗi có thể dừng chương trình. Trên đây là một số cách để fix lỗi encode trong ruby một cách dễ dàng. Tham khảo:


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í