Bạn chỉ thực sự nghĩ về mã hóa của một chuỗi khi nó bị hỏng. Khi bạn kiểm tra trình theo dõi ngoại lệ của mình và thấy
Encoding::InvalidByteSequenceError: "\xFE" on UTF-8
nhìn chằm chằm vào mặt bạn. Hoặc có thể “họ đang” bắt đầu hiển thị là “họ đang ở”.
Vì vậy, khi bạn có một bảng mã xấu, làm thế nào để bạn tìm ra những gì đã bị hỏng? Và bạn có thể sửa nó như thế nào?
Mã hóa là gì?
Nếu bạn có thể hình dung những gì mã hóa đối với một chuỗi, thì những lỗi này sẽ dễ sửa hơn.
Bạn có thể coi một chuỗi là một mảng byte hoặc số nhỏ:
irb(main):001:0> "hello!".bytes
=> [104, 101, 108, 108, 111, 33]
Trong bảng mã này, 104
nghĩa là h
, 33
nghĩa là !
, và như vậy.
Sẽ phức tạp hơn khi bạn sử dụng các ký tự ít phổ biến hơn trong tiếng Anh:
irb(main):002:0> "hellṏ!".bytes
=> [104, 101, 108, 108, 225, 185, 143, 33]
Giờ đây, thật khó hơn để biết số nào đại diện cho ký tự nào. Thay vì một byte, ṏ
được đại diện bởi nhóm byte [225, 185, 143]
. Nhưng vẫn có mối quan hệ giữa byte và ký tự. Và mã hóa của một chuỗi xác định mối quan hệ đó.
Hãy xem một tập hợp các byte trông như thế nào khi bạn thử các mã hóa khác nhau:
# 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 byte không thay đổi. Nhưng điều đó có vẻ không ổn chút nào. Việc thay đổi mã hóa đã thay đổi cách in chuỗi mà không thay đổi byte.
Và không phải tất cả các chuỗi đều có thể được biểu diễn trong tất cả các 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>'
Hầu hết các mã hóa đều nhỏ và không thể xử lý mọi ký tự có thể. Bạn sẽ thấy lỗi đó khi một ký tự trong một bảng mã này không tồn tại trong một bảng mã khác hoặc khi Ruby không thể tìm ra cách dịch một ký tự giữa hai bảng mã.
Bạn có thể khắc phục lỗi này nếu bạn chuyển các tùy chọn bổ sung vào encode
:
irb(main):064:0> "hi∑".encode("Windows-1252", invalid: :replace, undef: :replace)
=> "hi?"
invalid
và undef
các tùy chọn thay thế các ký tự không thể dịch bằng một ký tự khác. Theo mặc định, ký tự thay thế đó là ?
. (Khi bạn chuyển đổi sang Unicode, đó là �).
Rất tiếc, khi bạn thay thế các ký tự bằng encode
, bạn có thể mất thông tin. Bạn không biết byte nào được thay thế bằng ?
. Nhưng nếu bạn cần dữ liệu của mình ở dạng mã hóa mới đó, thì việc mất dữ liệu có thể tốt hơn là mọi thứ bị hỏng.
Cho đến nay, bạn đã thấy ba phương pháp chuỗi khóa để giúp bạn hiểu các mã hóa:
-
encode
, dịch một chuỗi sang một kiểu mã hóa khác (chuyển đổi các ký tự thành ký tự tương đương trong bảng mã mới) -
bytes
, sẽ hiển thị cho bạn các byte tạo nên một chuỗi -
force_encoding
, sẽ cho bạn biết những byte đó sẽ trông như thế nào được diễn giải bằng một mã hóa khác
Sự khác biệt chính giữa encode
và force_encoding
đó có phải là encode
không có thể thay đổi bytes
và force_encoding
sẽ không.
Quy trình ba bước để sửa lỗi mã hóa
Bạn có thể khắc phục hầu hết các sự cố mã hóa bằng ba bước:
1. Khám phá xem mã hóa chuỗi của bạn thực sự là trong.
Điều này nghe có vẻ dễ dàng. Nhưng chỉ vì một chuỗi nói đó là một số mã hóa, không có nghĩa là nó thực sự là:
irb(main):078:0> "hi\x99!".encoding
=> #<Encoding:UTF-8>
Điều đó không đúng - nếu đó là thực sự UTF-8, nó sẽ không có số gạch chéo ngược kỳ lạ trong đó. Vậy làm cách nào để tìm ra mã hóa phù hợp cho chuỗi của bạn?
Nhiều phần mềm cũ hơn sẽ bám vào một bảng mã mặc định duy nhất, vì vậy bạn có thể nghiên cứu xem đầu vào đến từ đâu. Ai đó đã dán nó vào từ Word? Nó có thể là Windows-1252. Nó đến từ một tệp hay bạn lấy nó từ một trang web cũ hơn? Nó có thể là ISO-8859-1.
Tôi cũng thấy hữu ích khi tìm kiếm các bảng mã hóa, như bảng mã trên các trang Wikipedia được liên kết đó. Trên các bảng đó, bạn có thể tra cứu các ký tự được tham chiếu bởi các số không xác định và xem chúng có hợp lý trong ngữ cảnh hay không.
Trong ví dụ này, biểu đồ Windows-1252 cho thấy rằng byte 99
đại diện cho ký tự “™”. Byte 99
không tồn tại theo ISO-8859-1. Nếu ™ có ý nghĩa ở đây, bạn có thể cho rằng đầu vào là trong Windows-1252 và tiếp tục. Nếu không, bạn có thể tiếp tục nghiên cứu cho đến khi tìm thấy một nhân vật có vẻ hợp lý hơn.
2. Quyết định kiểu mã hóa bạn muốn chuỗi được.
Điều này thật dễ dàng. Trừ khi bạn có lý do thực sự chính đáng, nếu không bạn muốn chuỗi của mình được mã hóa UTF-8.
Có một mã hóa phổ biến khác mà bạn có thể sử dụng trong Ruby:ASCII-8BIT. Trong ASCII-8BIT, mọi ký tự được biểu diễn bằng một byte duy nhất. Đó là, str.chars.length == str.bytes.length
. Vì vậy, nếu bạn muốn kiểm soát nhiều byte cụ thể trong chuỗi của mình, ASCII-8BIT có thể là một lựa chọn tốt.
3. Mã hóa lại chuỗi của bạn từ mã hóa ở bước 1 sang mã hóa ở bước 2.
Bạn có thể thực hiện việc này với encode
phương pháp. Trong ví dụ này, chuỗi của chúng tôi was trong mã hóa Windows-1252 và chúng tôi muốn nó trở thành UTF-8. Khá đơn giản:
irb(main):088:0> "hi\x99!".encode("UTF-8", "Windows-1252")
=> "hi™!"
Tốt hơn nhiều. (Mặc dù thứ tự của các mã hóa trong lệnh gọi đó luôn có vẻ ngược với tôi).
Có thể khiến bạn phải căng não để tưởng tượng ra các cách hiểu khác nhau của cùng một mảng byte. Đặc biệt khi một trong những cách hiểu đó bị hỏng. Nhưng có một cách tuyệt vời để trở nên thoải mái hơn rất nhiều với các mã hóa:Chơi với chúng.
Mở irb
bảng điều khiển và lộn xộn với encode
, bytes
và force_encoding
. Xem cách mã hóa encode
thay đổi các byte tạo nên chuỗi. Xây dựng trực giác về các mã hóa khác nhau trông như thế nào. Khi bạn đã cảm thấy thoải mái hơn với các mã hóa và sử dụng các bước này, bạn sẽ khắc phục được điều mà bạn đã mất hàng giờ trước đó trong vài phút.
Cuối cùng, nếu bạn muốn học cách tạo thói quen từ việc học những thứ này bằng cách thực hiện, hãy lấy chương mẫu miễn phí của cuốn sách của tôi. Phá vỡ mọi thứ trong bảng điều khiển là một thực sự cách thú vị để nghiên cứu những ý tưởng như thế này.