Lỗi utf 8 python

Đây là bài thuyết trình tôi đã trình bày tại PyCon 2012. Bạn có thể đọc các trang trình bày và văn bản trên trang này hoặc mở bản trình bày thực tế trong trình duyệt của mình hoặc xem video

Ngoài ra, nhấp vào hình ảnh trang trình bày sẽ chuyển sang bản trình bày đầy đủ tại thời điểm đó. Phông chữ Symbola được bao gồm, nhưng sẽ phải tải xuống trước khi một số ký hiệu đặc biệt xuất hiện

Xin chào, tôi là Ned Batchelder. Tôi đã viết bằng Python hơn mười năm, có nghĩa là ít nhất nửa tá lần, tôi đã mắc phải những lỗi Unicode giống như những người khác mắc phải

Nếu bạn giống như hầu hết các lập trình viên Python, thì bạn cũng đã làm được. bạn đã xây dựng một ứng dụng hay và mọi thứ dường như đang diễn ra tốt đẹp. Rồi một ngày nọ, một ký tự có dấu đột nhiên xuất hiện, và chương trình của bạn bắt đầu báo lỗi UnicodeErrors

Bạn gần như biết phải làm gì với những thứ đó, vì vậy bạn đã thêm mã hóa hoặc giải mã vào nơi xảy ra lỗi, nhưng Lỗi Unicode lại xảy ra ở một nơi khác. Bạn đã đến nơi mới và thêm một bản giải mã, có thể là một bản mã hóa. Sau khi chơi đập chuột như thế này một lúc, vấn đề dường như đã được khắc phục

Sau đó vài ngày, một giọng khác xuất hiện ở một chỗ khác, và bạn phải chơi thêm một chút nữa cho đến khi vấn đề cuối cùng dừng lại

Vì vậy, bây giờ bạn có một chương trình hoạt động, nhưng bạn cảm thấy khó chịu và không thoải mái, mất quá nhiều thời gian, bạn biết nó không “đúng” và bạn ghét chính mình. Và điều chính bạn biết về Unicode là bạn không thích Unicode

Bạn không muốn biết về các bộ ký tự lập dị, bạn chỉ muốn có thể viết một chương trình không khiến bạn cảm thấy tồi tệ

Bạn không cần phải chơi trò đập chuột chũi. Unicode không đơn giản nhưng cũng không khó. Với kiến ​​thức và kỷ luật, bạn có thể xử lý Unicode dễ dàng và duyên dáng

Tôi sẽ dạy cho bạn năm Sự thật của cuộc sống và cung cấp cho bạn ba Mẹo chuyên nghiệp sẽ giải quyết các vấn đề về Unicode của bạn. Chúng tôi sẽ đề cập đến những điều cơ bản về Unicode và cách cả Python 2 và Python 3 hoạt động. Chúng khác nhau, nhưng các chiến lược bạn sẽ sử dụng về cơ bản là giống nhau

Thế giới & Unicode

Chúng ta sẽ bắt đầu với những kiến ​​thức cơ bản về Unicode

Sự thật đầu tiên của cuộc sống. mọi thứ trong máy tính đều là byte. Các tệp trên đĩa là một chuỗi byte và kết nối mạng chỉ truyền byte. Hầu như không có ngoại lệ, tất cả dữ liệu vào hoặc ra khỏi bất kỳ chương trình nào bạn viết đều là byte

Vấn đề với byte là bản thân chúng vô nghĩa, chúng ta cần các quy ước để mang lại ý nghĩa cho chúng

Để thể hiện văn bản, chúng tôi đã sử dụng mã ASCII trong gần 50 năm. Mỗi byte được gán một trong 95 ký hiệu. Khi tôi gửi cho bạn một byte 65, bạn biết rằng ý tôi là chữ A viết hoa, nhưng chỉ vì chúng ta đã thống nhất trước về nội dung của mỗi byte

ISO Latin 1, hoặc 8859-1, là ASCII được mở rộng với 96 ký hiệu khác

Windows đã thêm 27 biểu tượng khác để tạo ra CP1252. Đây là cách tốt nhất bạn có thể làm để biểu thị văn bản dưới dạng byte đơn, vì không còn nhiều chỗ để thêm các ký hiệu khác

Với các bộ ký tự như thế này, chúng tôi có thể đại diện cho tối đa 256 ký tự. Nhưng Sự thật cuộc sống số 2 là có hơn 256 biểu tượng trong văn bản trên thế giới. Một byte đơn giản không thể đại diện cho văn bản trên toàn thế giới. Trong những khoảnh khắc đen tối nhất của bạn, bạn có thể ước rằng mọi người đều nói tiếng Anh, nhưng đơn giản là không phải vậy. Mọi người cần rất nhiều biểu tượng để giao tiếp

Fact of Life #1 và Fact of Life #2 cùng nhau tạo ra xung đột cơ bản giữa cấu trúc của các thiết bị máy tính của chúng ta và nhu cầu của mọi người trên thế giới

Đã có một số nỗ lực để giải quyết xung đột này. Mã ký tự một byte như ASCII ánh xạ byte thành ký tự. Mỗi người đều giả vờ rằng Fact of Life #2 không tồn tại

Có nhiều mã byte đơn và chúng không giải quyết được vấn đề. Mỗi cái chỉ tốt cho việc đại diện cho một lát cắt nhỏ của ngôn ngữ loài người. Họ không thể giải quyết vấn đề văn bản toàn cầu

Mọi người đã thử tạo các bộ ký tự hai byte, nhưng chúng vẫn bị phân mảnh, phục vụ các nhóm người dùng khác nhau. Có nhiều tiêu chuẩn được đưa ra và chúng vẫn chưa đủ lớn để xử lý tất cả các biểu tượng cần thiết

Unicode được thiết kế để giải quyết triệt để các vấn đề với mã ký tự cũ hơn. Unicode gán số nguyên, được gọi là điểm mã, cho các ký tự. Nó có chỗ cho 1. 1 triệu điểm mã và chỉ 110.000 điểm đã được chỉ định, vì vậy có rất nhiều chỗ cho sự phát triển trong tương lai

Mục tiêu của Unicode là có mọi thứ. Nó bắt đầu với ASCII, và bao gồm hàng ngàn biểu tượng, bao gồm cả Người tuyết nổi tiếng, bao gồm tất cả các hệ thống chữ viết trên thế giới và không ngừng được mở rộng. Ví dụ: bản cập nhật mới nhất đã cho chúng tôi biểu tượng PILE OF POO

Đây là một chuỗi sáu ký tự Unicode kỳ lạ. Các điểm mã Unicode được viết dưới dạng 4-, 5- hoặc 6 chữ số hex với tiền tố U+. Mỗi ký tự có một tên đầy đủ rõ ràng luôn ở dạng chữ hoa ASCII

Chuỗi này được thiết kế để trông giống như từ “Python”, nhưng hoàn toàn không sử dụng bất kỳ ký tự ASCII nào

Vì vậy, Unicode nhường chỗ cho tất cả các ký tự mà chúng ta có thể cần, nhưng chúng ta vẫn có Fact of Life #1 để giải quyết. máy tính cần byte. Chúng tôi cần một cách để biểu thị các điểm mã Unicode dưới dạng byte để lưu trữ hoặc truyền tải chúng

Tiêu chuẩn Unicode xác định một số cách để biểu thị các điểm mã dưới dạng byte. Chúng được gọi là mã hóa

UTF-8 dễ dàng là mã hóa phổ biến nhất để lưu trữ và truyền Unicode. Nó sử dụng một số byte thay đổi cho mỗi điểm mã. Giá trị điểm mã càng cao thì càng cần nhiều byte trong UTF-8. Mỗi ký tự ASCII là một byte, sử dụng các giá trị giống như ASCII, vì vậy ASCII là tập hợp con của UTF-8

Ở đây chúng tôi hiển thị chuỗi kỳ lạ của mình dưới dạng UTF-8. Các ký tự ASCII H và i là các byte đơn và các ký tự khác sử dụng hai hoặc ba byte tùy thuộc vào giá trị điểm mã của chúng. Một số điểm mã Unicode yêu cầu bốn byte, nhưng chúng tôi không sử dụng bất kỳ điểm nào ở đây

Trăn 2

OK, lý thuyết đủ rồi, hãy nói về Python 2. Trong các trang trình bày, mẫu Python 2 có số 2 lớn ở góc trên bên phải, mẫu Python 3 sẽ có số 3 lớn

Trong Python 2, có hai kiểu dữ liệu chuỗi khác nhau. Một chuỗi ký tự cũ đơn giản cung cấp cho bạn một đối tượng “str”, lưu trữ các byte. Nếu bạn sử dụng tiền tố “u”, bạn sẽ nhận được một đối tượng “unicode”, lưu trữ các điểm mã. Trong một chuỗi ký tự unicode, bạn có thể sử dụng dấu gạch chéo ngược-u để chèn bất kỳ điểm mã Unicode nào

Lưu ý rằng từ "chuỗi" có vấn đề. Cả “str” và “unicode” đều là các loại chuỗi và thật hấp dẫn khi gọi một trong hai hoặc cả hai chúng là “chuỗi”, nhưng tốt hơn là sử dụng các thuật ngữ cụ thể hơn để giữ mọi thứ rõ ràng

Mỗi chuỗi byte và chuỗi unicode đều có một phương thức để chuyển đổi nó thành loại chuỗi khác. Chuỗi Unicode có một. encode[] tạo byte và chuỗi byte có. phương thức giải mã [] tạo unicode. Mỗi cái lấy một đối số, là tên của mã hóa sẽ sử dụng cho thao tác

Chúng ta có thể định nghĩa một chuỗi Unicode có tên my_unicode và thấy rằng nó có 9 ký tự. Chúng ta có thể mã hóa nó thành UTF-8 để tạo chuỗi byte my_utf8, có 19 byte. Như bạn mong đợi, việc đảo ngược thao tác bằng cách giải mã chuỗi UTF-8 sẽ tạo ra chuỗi Unicode gốc

Thật không may, mã hóa và giải mã có thể tạo ra lỗi nếu dữ liệu không phù hợp với mã hóa đã chỉ định. Ở đây chúng tôi cố gắng mã hóa chuỗi Unicode kỳ lạ của mình thành ASCII. Không thành công vì ASCII chỉ có thể biểu thị các ký tự trong phạm vi từ 0 đến 127 và chuỗi Unicode của chúng tôi có các điểm mã nằm ngoài phạm vi đó

Lỗi UnicodeEncodeError xuất hiện cho biết mã hóa đang được sử dụng, ở dạng “codec” [viết tắt của bộ mã hóa/bộ giải mã] và vị trí thực tế của ký tự gây ra sự cố

Giải mã cũng có thể tạo ra lỗi. Ở đây, chúng tôi cố gắng giải mã chuỗi UTF-8 của mình dưới dạng ASCII và nhận được UnicodeDecodeError vì một lần nữa, ASCII chỉ có thể chấp nhận các giá trị tối đa 127 và chuỗi UTF-8 của chúng tôi có các byte nằm ngoài phạm vi đó

Ngay cả UTF-8 cũng không thể giải mã bất kỳ chuỗi byte nào. Tiếp theo, chúng tôi cố gắng giải mã một số rác ngẫu nhiên và nó cũng tạo ra lỗi UnicodeDecodeError. Trên thực tế, một trong những ưu điểm của UTF-8 là có các chuỗi byte không hợp lệ, giúp xây dựng các hệ thống mạnh mẽ. lỗi trong dữ liệu sẽ không được chấp nhận như thể chúng hợp lệ

Khi mã hóa hoặc giải mã, bạn có thể chỉ định điều gì sẽ xảy ra khi codec không thể xử lý dữ liệu. Đối số thứ hai tùy chọn để mã hóa hoặc giải mã chỉ định chính sách. Giá trị mặc định là "nghiêm ngặt", có nghĩa là gây ra lỗi, như chúng ta đã thấy

Giá trị của "thay thế" có nghĩa là, hãy cho tôi một ký tự thay thế tiêu chuẩn. Khi mã hóa, ký tự thay thế là một dấu chấm hỏi, do đó, bất kỳ điểm mã nào không thể được mã hóa bằng cách sử dụng mã hóa đã chỉ định sẽ chỉ tạo ra dấu “?”

Các trình xử lý lỗi khác hữu ích hơn. “xmlcharreplace” tạo tham chiếu thực thể ký tự HTML/XML, để \u01B4 trở thành “ƴ” [hex 01B4 là số thập phân 436. ] Điều này rất hữu ích nếu bạn cần xuất unicode cho tệp HTML

Lưu ý rằng các chính sách lỗi khác nhau được sử dụng vì những lý do khác nhau. “Thay thế” là một cơ chế phòng thủ chống lại dữ liệu không thể diễn giải và mất thông tin. “Xmlcharreplace” lưu giữ tất cả thông tin gốc và được sử dụng khi xuất dữ liệu trong đó có thể chấp nhận thoát XML

Bạn cũng có thể chỉ định xử lý lỗi khi giải mã. “Bỏ qua” sẽ loại bỏ các byte không thể giải mã chính xác. “Thay thế” sẽ chèn Unicode U+FFFD, “THỰC PHẨM THAY THẾ” cho các byte có vấn đề. Lưu ý rằng vì bộ giải mã không thể giải mã dữ liệu, nên nó không biết có bao nhiêu ký tự Unicode được dự định. Việc giải mã các byte UTF-8 của chúng tôi dưới dạng ASCII tạo ra 16 ký tự thay thế, một ký tự thay thế cho mỗi byte không thể giải mã được, trong khi các byte đó chỉ có nghĩa là tạo ra 6 ký tự Unicode

Python 2 cố gắng trở nên hữu ích khi làm việc với chuỗi byte và unicode. Nếu bạn cố gắng thực hiện thao tác chuỗi kết hợp chuỗi unicode với chuỗi byte, Python 2 sẽ tự động giải mã chuỗi byte để tạo chuỗi unicode thứ hai, sau đó sẽ hoàn tất thao tác với hai chuỗi unicode

Ví dụ: chúng tôi cố gắng nối một unicode “Xin chào” với một chuỗi byte “thế giới”. Kết quả là một unicode “Xin chào thế giới”. Thay mặt chúng tôi, Python 2 đang giải mã chuỗi byte “thế giới” bằng codec ASCII. Mã hóa được sử dụng cho các giải mã ẩn này là giá trị của sys. getdefaultencoding[]

Mã hóa ẩn là ASCII vì đó là dự đoán an toàn duy nhất. ASCII được chấp nhận rộng rãi và là một tập hợp con của rất nhiều bảng mã nên không có khả năng tạo ra thông tin sai lệch

Tất nhiên, những giải mã ngầm này không tránh khỏi lỗi giải mã. Nếu bạn cố gắng kết hợp một chuỗi byte với một chuỗi unicode và chuỗi byte đó không thể được giải mã thành ASCII, thì thao tác này sẽ gây ra lỗi UnicodeDecodeError

Đây là nguồn gốc của những UnicodeErrors đau đớn đó. Mã của bạn vô tình trộn lẫn các chuỗi unicode và chuỗi byte và miễn là dữ liệu đều là ASCII, các chuyển đổi ngầm sẽ âm thầm thành công. Khi một ký tự không phải ASCII tìm được đường vào chương trình của bạn, quá trình giải mã ẩn sẽ không thành công, gây ra lỗi UnicodeDecodeError

Triết lý của Python 2 là các chuỗi unicode và chuỗi byte gây nhầm lẫn và nó đã cố gắng giảm bớt gánh nặng cho bạn bằng cách tự động chuyển đổi giữa chúng, giống như đối với int và float. Nhưng việc chuyển đổi từ int sang float không thể thất bại, trong khi chuỗi byte thành chuỗi unicode có thể

Python 2 âm thầm chuyển đổi từ byte sang unicode, giúp viết mã liên quan đến ASCII dễ dàng hơn nhiều. Cái giá bạn phải trả là nó sẽ thất bại với dữ liệu không phải ASCII

Có rất nhiều cách để kết hợp hai chuỗi và tất cả chúng sẽ giải mã byte thành unicode, vì vậy bạn phải coi chừng chúng

Ở đây chúng tôi sử dụng chuỗi định dạng ASCII, với dữ liệu unicode. Chuỗi định dạng sẽ được giải mã thành unicode, sau đó định dạng được thực hiện, dẫn đến chuỗi unicode

Tiếp theo, chúng tôi chuyển đổi hai. Một chuỗi định dạng unicode và một chuỗi byte lại kết hợp để tạo ra một chuỗi unicode, vì dữ liệu chuỗi byte được giải mã thành ASCII

Ngay cả khi chỉ cố in một chuỗi unicode cũng sẽ gây ra mã hóa ngầm. đầu ra luôn là byte, vì vậy chuỗi unicode phải được mã hóa thành byte trước khi có thể in

Cái tiếp theo thực sự khó hiểu. chúng tôi yêu cầu mã hóa một chuỗi byte thành UTF-8 và gặp lỗi về việc không sắp giải mã dưới dạng ASCII. Vấn đề ở đây là chuỗi byte không thể được mã hóa. hãy nhớ mã hóa là cách bạn biến unicode thành byte. Vì vậy, để thực hiện mã hóa mà bạn muốn, Python 2 cần một chuỗi unicode mà nó cố lấy bằng cách giải mã ngầm các byte của bạn dưới dạng ASCII

Vì vậy, bạn đã yêu cầu mã hóa thành UTF-8 và bạn gặp lỗi khi giải mã ASCII. Nó đáng để xem xét lỗi một cách cẩn thận, nó có manh mối về hoạt động nào đang được thực hiện và nó đã thất bại như thế nào

Cuối cùng, chúng tôi mã hóa một chuỗi ASCII thành UTF-8, điều này thật ngớ ngẩn, mã hóa nên được sử dụng trên chuỗi unicode. Để làm cho nó hoạt động, Python thực hiện giải mã ngầm tương tự để lấy một chuỗi unicode mà chúng ta có thể mã hóa, nhưng vì chuỗi đó là ASCII nên nó thành công và sau đó tiếp tục mã hóa nó thành UTF-8, tạo ra chuỗi byte gốc, vì ASCII

Đây là sự thật quan trọng nhất của cuộc sống. byte và unicode đều quan trọng và bạn cần xử lý cả hai. Bạn không thể giả vờ rằng mọi thứ đều là byte hoặc mọi thứ đều là unicode. Bạn cần sử dụng từng mục đích của chúng và chuyển đổi rõ ràng giữa chúng khi cần

Trăn 3

Chúng ta đã thấy nguồn gốc của nỗi đau Unicode trong Python 2, bây giờ hãy xem Python 3. Thay đổi lớn nhất từ ​​Python 2 sang Python 3 là cách họ xử lý Unicode

Cũng giống như Python 2, Python 3 có hai loại chuỗi, một cho unicode và một cho byte, nhưng chúng được đặt tên khác nhau

Bây giờ, loại “str” mà bạn nhận được từ một chuỗi ký tự đơn giản lưu trữ unicode và loại “byte” lưu trữ byte. Bạn có thể tạo một byte bằng chữ với tiền tố b

Vì vậy, “str” trong Python 2 hiện được gọi là “byte” và “unicode” trong Python 2 hiện được gọi là “str”. Điều này có ý nghĩa hơn tên Python 2, vì Unicode là cách bạn muốn tất cả văn bản được lưu trữ và chuỗi byte chỉ dành cho khi bạn xử lý byte

Thay đổi lớn nhất trong hỗ trợ Unicode trong Python 3 là không có giải mã tự động chuỗi byte. Nếu bạn cố gắng kết hợp một chuỗi byte với một chuỗi unicode, bạn sẽ luôn gặp lỗi, bất kể dữ liệu liên quan là gì

Tất cả các hoạt động mà tôi đã chỉ ra trong đó Python 2 âm thầm chuyển đổi chuỗi byte thành chuỗi unicode để hoàn thành một hoạt động, mỗi thao tác đều là lỗi trong Python 3

Ngoài ra, Python 2 coi chuỗi unicode và chuỗi byte bằng nhau nếu chúng chứa cùng byte ASCII và Python 3 sẽ không. Hậu quả của việc này là không thể tìm thấy các khóa từ điển unicode bằng chuỗi byte và ngược lại, vì chúng có thể có trong Python 2

Điều này thay đổi đáng kể bản chất của nỗi đau Unicode trong Python 3. Trong Python 2, trộn unicode và byte thành công miễn là bạn chỉ sử dụng dữ liệu ASCII. Trong Python 3, nó bị lỗi ngay lập tức bất kể dữ liệu

Vì vậy, nỗi đau của Python 2 được hoãn lại. bạn nghĩ rằng chương trình của bạn là chính xác và sau đó phát hiện ra rằng nó bị lỗi với các ký tự kỳ lạ

Với Python 3, mã của bạn bị lỗi ngay lập tức, vì vậy ngay cả khi bạn chỉ xử lý ASCII, bạn phải xử lý rõ ràng sự khác biệt giữa byte và unicode

Python 3 nghiêm ngặt về sự khác biệt giữa byte và unicode. Bạn buộc phải rõ ràng trong mã mà bạn đang xử lý. Điều này đã gây tranh cãi và có thể khiến bạn đau đớn

Do tính nghiêm ngặt mới này, Python 3 đã thay đổi cách bạn đọc tệp. Python luôn có hai chế độ đọc tệp. nhị phân và văn bản. Trong Python 2, nó chỉ ảnh hưởng đến phần cuối của dòng và trên nền tảng Unix, thậm chí đó là một lệnh cấm.

Trong Python 3, hai chế độ tạo ra kết quả khác nhau. Khi bạn mở một tệp ở chế độ văn bản, bằng “r” hoặc bằng cách đặt chế độ mặc định hoàn toàn, dữ liệu đọc từ tệp được giải mã hoàn toàn thành Unicode và bạn nhận được các đối tượng str

Nếu bạn mở một tệp ở chế độ nhị phân, bằng cách cung cấp “rb” làm chế độ, thì dữ liệu được đọc từ tệp là byte, không có quá trình xử lý nào được thực hiện trên chúng

Việc chuyển đổi ngầm định từ byte sang unicode sử dụng mã hóa được trả về từ ngôn ngữ. getpreferredencoding[] và nó có thể không mang lại cho bạn kết quả như mong đợi. Ví dụ, khi chúng ta đọc hi_utf8. txt, nó đang được giải mã bằng cách sử dụng mã hóa ưa thích của ngôn ngữ, vì tôi đã tạo các mẫu này trên Windows, là “cp1252”. Giống như ISO 8859-1, CP-1252 là mã ký tự một byte sẽ chấp nhận bất kỳ giá trị byte nào, vì vậy nó sẽ không bao giờ gây ra lỗi UnicodeDecodeError. Điều đó cũng có nghĩa là nó sẽ vui vẻ giải mã dữ liệu không thực sự là CP-1252 và tạo ra rác

Để đọc tệp đúng cách, bạn nên chỉ định mã hóa để sử dụng. Hàm open[] hiện có tham số mã hóa tùy chọn

giảm đau

OK, vậy làm thế nào để chúng ta đối phó với tất cả nỗi đau này?

Như chúng ta đã thấy với Fact of Life #1, dữ liệu vào và ra khỏi chương trình của bạn phải là byte. Nhưng bạn không cần xử lý các byte bên trong chương trình của mình. Chiến lược tốt nhất là giải mã các byte đến càng sớm càng tốt, tạo ra unicode. Bạn sử dụng unicode trong suốt chương trình của mình và sau đó khi xuất dữ liệu, hãy mã hóa nó thành byte càng muộn càng tốt

Điều này tạo ra một bánh sandwich Unicode. byte ở bên ngoài, Unicode ở bên trong

Hãy nhớ rằng đôi khi thư viện bạn đang sử dụng có thể thực hiện một số chuyển đổi này cho bạn. Thư viện có thể cung cấp cho bạn đầu vào Unicode hoặc sẽ chấp nhận Unicode cho đầu ra và thư viện sẽ xử lý việc chuyển đổi cạnh sang và từ byte. Ví dụ: Django cung cấp Unicode, cũng như mô-đun json

Quy tắc thứ hai là bạn phải biết mình đang xử lý loại dữ liệu nào. Tại bất kỳ thời điểm nào trong chương trình của bạn, bạn cần biết mình có chuỗi byte hay chuỗi unicode. Đây không phải là vấn đề phỏng đoán, nó phải là do thiết kế

Ngoài ra, nếu bạn có một chuỗi byte, bạn nên biết mã hóa nó là gì nếu bạn có ý định xử lý nó dưới dạng văn bản

Khi gỡ lỗi mã của bạn, bạn không thể chỉ cần in một giá trị để xem nó là gì. Bạn cần xem loại và bạn có thể cần xem repr của giá trị để tìm hiểu tận cùng dữ liệu bạn có

Tôi đã nói bạn phải hiểu mã hóa chuỗi byte của bạn là gì. Đây là Thực tế cuộc sống #4. Bạn không thể xác định mã hóa của chuỗi byte bằng cách kiểm tra nó. Bạn cần biết thông qua các phương tiện khác. Ví dụ: nhiều giao thức bao gồm các cách để chỉ định mã hóa. Ở đây chúng tôi có các ví dụ từ các tệp nguồn HTTP, HTML, XML và Python. Bạn cũng có thể biết mã hóa bằng cách sắp xếp trước, ví dụ: thông số kỹ thuật cho nguồn dữ liệu có thể chỉ định mã hóa

Có nhiều cách để đoán mã hóa byte, nhưng chúng chỉ là phỏng đoán. Cách duy nhất để chắc chắn về mã hóa là tìm ra nó theo một số cách khác

Dưới đây là ví dụ về chuỗi Unicode kỳ lạ của chúng tôi, được mã hóa thành UTF-8, sau đó bị giải mã nhầm thành nhiều loại mã hóa. Như bạn có thể thấy, giải mã với mã hóa không chính xác có thể thành công, nhưng tạo ra các ký tự sai. Chương trình của bạn không thể biết nó đang giải mã sai, chỉ khi mọi người cố gắng đọc văn bản, bạn mới biết đã xảy ra sự cố

Đây là một minh chứng tốt cho Fact of Life #4. cùng một luồng byte có thể giải mã được bằng cách sử dụng một số mã hóa khác nhau. Bản thân các byte không cho biết chúng sử dụng mã hóa nào

Nhân tiện, có một thuật ngữ cho màn trình diễn rác này, từ những người Nhật đã giải quyết vấn đề này trong nhiều năm. Mojibake

Thật không may, vì mã hóa cho byte phải được giao tiếp riêng biệt với chính byte, nên đôi khi mã hóa được chỉ định bị sai. Ví dụ: bạn có thể kéo một trang HTML từ máy chủ web và tiêu đề HTTP xác nhận trang đó là 8859-1, nhưng trên thực tế, nó được mã hóa bằng UTF-8

Trong một số trường hợp, mã hóa không khớp sẽ thành công và gây ra mojibake. Những lần khác, mã hóa không hợp lệ đối với các byte và sẽ gây ra lỗi UnicodeError thuộc loại nào đó

Nó nên đi mà không cần nói. bạn nên kiểm tra rõ ràng sự hỗ trợ Unicode của mình. Để làm điều này, bạn cần có dữ liệu Unicode đầy thách thức để chuyển qua mã của bạn. Nếu bạn là người chỉ nói tiếng Anh, bạn có thể gặp sự cố khi thực hiện việc này vì rất khó đọc dữ liệu không phải ASCII. May mắn thay, sự đa dạng của các điểm mã Unicode có nghĩa là bạn có thể tạo các chuỗi Unicode phức tạp mà người nói tiếng Anh vẫn có thể đọc được

Dưới đây là một ví dụ về văn bản quá nhiều dấu, văn bản giả ASCII có thể đọc được và văn bản lộn ngược. Một nguồn tốt của các loại chuỗi này là các trang web khác nhau cung cấp các chuỗi như thế này để thanh thiếu niên dán vào các trang mạng xã hội

Tùy thuộc vào ứng dụng của bạn, bạn có thể cần tìm hiểu sâu hơn về các vấn đề phức tạp khác trong thế giới Unicode. Có nhiều chi tiết mà tôi chưa đề cập ở đây và chúng có thể rất liên quan. Tôi gọi đây là Thực tế cuộc sống #5½ bởi vì bạn có thể không phải đối mặt với bất kỳ vấn đề nào trong số này

Để xem lại, đây là năm sự thật không thể tránh khỏi của cuộc sống

  1. Tất cả đầu vào và đầu ra của chương trình của bạn là byte
  2. Thế giới cần hơn 256 ký hiệu để giao tiếp văn bản
  3. Chương trình của bạn phải xử lý cả byte và Unicode
  4. Luồng byte không thể cho bạn biết mã hóa của nó
  5. Thông số kỹ thuật mã hóa có thể sai

Đây là ba Mẹo chuyên nghiệp cần ghi nhớ khi bạn xây dựng phần mềm của mình để giữ cho mã của bạn sạch Unicode

  1. bánh sandwich Unicode. giữ tất cả văn bản trong chương trình của bạn dưới dạng Unicode và chuyển đổi càng gần các cạnh càng tốt
  2. Biết chuỗi của bạn là gì. bạn sẽ có thể giải thích chuỗi nào của bạn là Unicode, chuỗi nào là byte và đối với chuỗi byte của bạn, chúng sử dụng mã hóa nào
  3. Kiểm tra hỗ trợ Unicode của bạn. Sử dụng các chuỗi kỳ lạ trong các bộ thử nghiệm của bạn để đảm bảo rằng bạn đang xử lý tất cả các trường hợp

Nếu bạn làm theo những lời khuyên này, bạn sẽ viết mã vững chắc, xử lý tốt với Unicode và sẽ không bị lỗi cho dù nó gặp phải Unicode khó hiểu như thế nào

Các tài nguyên khác mà bạn có thể thấy hữu ích

Joel Spolsky đã viết Điều tối thiểu tuyệt đối mà mọi nhà phát triển phần mềm phải biết về Unicode và bộ ký tự [Không bào chữa. ], bao gồm cách Unicode hoạt động và tại sao. Nó không có thông tin cụ thể về Python, nhưng được viết tốt hơn bài nói chuyện này

Nếu bạn cần xử lý ngữ nghĩa của các ký tự Unicode tùy ý, mô-đun unicodedata trong thư viện chuẩn Python có các chức năng có thể trợ giúp

Chủ Đề