Trong bài viết tuần trước, bạn đã biết một quy trình ngắn giải quyết hầu hết các vấn đề về mã hóa. Nhưng có một vấn đề mã hóa khó giải quyết hơn nhiều.
Tôi biết bạn đã nhìn thấy nó. (Hoặc có thể bạn đã xem?) Đó là khi một dấu ngoặc kép chuyển thành â € ™ hoặc một dấu gạch ngang chuyển thành "". Nó sẽ khiến bạn nghĩ rằng bạn đã phát điên. Nó sẽ hoạt động!
Bạn có thể tạo một bảng khổng lồ, vì vậy bạn có thể tìm thấy các ký tự xấu và thay thế chúng bằng các ký tự tốt:
[{broken: '–', fixed: "—"}
{broken: "—", fixed: "–"}
{broken: "‘", fixed: "‘"}
{broken: "’", fixed: "’"}
{broken: "“", fixed: "“"}
{broken: "â€", fixed: "”"}, ...]
Nhưng có một cách dễ dàng hơn, đáng tin cậy hơn để sửa những ký tự bị hỏng đó.
Tại sao kiểu chữ đẹp luôn bị vỡ?
Tuần trước, bạn đã biết rằng mã hóa chỉ là một cách để biến các nhóm byte vô nghĩa thành các ký tự có thể hiển thị. Không phải mọi ký tự đều có thể được biểu diễn trong một byte đơn lẻ, vì có thể có hơn 256 ký tự. Vì vậy, một số ký tự, như dấu ngoặc kép ’
, được biểu diễn bằng nhiều hơn một byte:
irb(main):001:0> "they’re".bytes
=> [116, 104, 101, 121, 226, 128, 153, 114, 101]
Mặc dù chuỗi chỉ có 7 ký tự nhưng chúng được thể hiện bằng 9 byte!
Khi bạn chỉ tập trung vào dấu ngoặc kép:
irb(main):002:0> "’".bytes
=> [226, 128, 153]
Bạn sẽ thấy nó sử dụng 3 byte. Và chuỗi lộn xộn của chúng tôi, chúng, có ba ký tự mà nó chỉ nên có một ký tự. Đó có vẻ như nhiều hơn là một sự trùng hợp, phải không?
Có vẻ như ba byte đó phải được đọc là UTF-8, nơi chúng đại diện cho một dấu ngoặc kép. Thay vào đó, từng byte đang hiển thị dưới dạng một nhân vật khác. Vì vậy, mã hóa nào sẽ đại diện cho [226, 128, 153]
dưới dạng ’
? Nếu bạn nhìn vào một vài bảng mã hóa phổ biến, bạn sẽ thấy đó là Windows-1252.
Bạn có thể kiểm tra điều này trong irb
:
irb(main):003:0> "they’re".force_encoding("Windows-1252").encode("UTF-8")
=> "they’re"
(Chúng tôi cần .encode("UTF-8")
cuối cùng đó để hiển thị chuỗi trong bảng điều khiển.)
Đúng! Đó là vấn đề. Nhưng nó trở nên tồi tệ hơn.
Dữ liệu được cho là UTF-8, nhưng đang bị đọc nhầm thành Windows-1252. Nhưng có thể bạn sẽ lưu dữ liệu đó vào cơ sở dữ liệu hoặc tệp dưới dạng UTF-8. Ruby sẽ hữu ích chuyển đổi nó thành UTF-8 cho bạn, vì vậy bạn sẽ kết thúc với:
irb(main):004:0> "they’re".force_encoding("Windows-1252").encode("UTF-8")
=> "they’re"
irb(main):005:0> "they’re".force_encoding("Windows-1252").encode("UTF-8").bytes
=> [116, 104, 101, 121, 195, 162, 226, 130, 172, 226, 132, 162, 114, 101]
Chuỗi của bạn đã được mã hóa sai hai lần . Những ký tự bị hỏng đó bây giờ trông giống như chúng đáng lẽ phải ở đó. Và nếu bạn không biết nó xảy ra như thế nào, thì hầu như không thể gỡ rối nó.
Bạn khắc phục nó như thế nào?
Làm thế nào để bạn đưa mọi thứ trở lại bình thường? Hãy nghĩ về vấn đề ngược lại:
-
Bạn có một chuỗi UTF-8, (chúng đang)
-
được chuyển đổi từ chuỗi Windows-1252, (chúng lại)
-
byte của ai lẽ ra phải là đọc là UTF-8 (chúng là)
Để khắc phục, bạn chỉ cần làm theo các bước ngược lại. Sử dụng mã hóa encode
để chuyển đổi chuỗi UTF-8 trở lại thành chuỗi Windows-1252. Sau đó, sử dụng force_encoding
để buộc chuỗi Windows-1252 được mã hóa sai đó được đọc là UTF-8:
irb(main):006:0> "they’re".encode("Windows-1252").force_encoding("UTF-8")
=> "they’re"
Đã sửa!
Có một vấn đề nhỏ…
Thật không may, bạn có thể tìm thấy sự cố này vì một loạt tệp hoặc bản ghi cơ sở dữ liệu có dữ liệu được mã hóa không tốt trong đó. Và không phải mọi tệp hoặc bản ghi nhất thiết phải được mã hóa kém - bạn có thể có sự kết hợp của dữ liệu tốt và xấu. Đặc biệt nếu dữ liệu đó đến từ những người truy cập trang web của bạn.
Nếu đúng như vậy, bạn không thể chạy mã đó một cách mù quáng trên mọi chuỗi:
irb(main):007:0> "they’re".encode("Windows-1252").force_encoding("UTF-8")
=> "they’re"
irb(main):008:0> "they’re".encode("Windows-1252").force_encoding("UTF-8")
=> "they\x92re"
Nếu bạn chạy nó trên dữ liệu tốt, bạn sẽ chỉ biến nó thành dữ liệu xấu. Vậy bạn có thể làm gì?
Bạn có thể sử dụng heuristic:chỉ thay đổi các chuỗi có một trong các ký tự không hợp lệ, chẳng hạn như â
. Điều này hoạt động tốt nếu một ký tự như â
sẽ không bao giờ xuất hiện trong một chuỗi hợp lệ.
Tuy nhiên, lần cuối cùng tôi đã sửa loại lỗi này, tôi muốn chơi nó một cách an toàn. Tôi đã sử dụng một công cụ hữu ích khác để trợ giúp:đôi mắt của tôi.
Bất cứ khi nào tôi tìm thấy một chuỗi được mã hóa sai, tôi đã in nó ra cùng với chuỗi thay thế:
Changing title with ID 6 from "They’re over there!" to "They’re over there!"
Bằng cách đó, tôi có thể kiểm tra ngay số lượng nhỏ các chuỗi đã thay đổi và đảm bảo rằng chúng không bị đứt thêm nữa.
Tôi nghĩ tôi bị đau đầu
Giống như tôi đã nói tuần trước, việc ghi nhớ những cách diễn giải khác nhau của cùng một dữ liệu trong đầu bạn thật khó! Nhưng nếu bạn bối rối, hãy khám phá trong irb
bàn điều khiển sẽ giúp. Vì vậy, hãy thử nó ra! Mở một tài khoản và xem liệu bạn có thể chuyển đổi qua lại giữa —
và —
hoặc “
và “
.
Thực hành những ý tưởng phức tạp như thế này là cách nhanh nhất để bạn cảm thấy tự tin khi cần chúng. Và trong chương mẫu miễn phí của Thực hành đường ray , bạn sẽ học các kỹ thuật và quy trình tốt nhất để thực hiện điều đó.