UTF-8 được chuẩn hóa tất cả về cái gì?


129

Các dự án ICU (mà bây giờ cũng có một thư viện PHP ) chứa các lớp cần thiết để giúp bình thường hóa UTF 8-strings để làm cho nó dễ dàng hơn để so sánh giá trị khi tìm kiếm.

Tuy nhiên, tôi đang cố gắng tìm hiểu điều này có nghĩa gì cho các ứng dụng. Ví dụ: trong trường hợp nào tôi muốn "Tương đương Canonical" thay vì "Tương đương tương thích", hoặc ngược lại?


230
Ai ̸͢k̵͟n̴͘ǫw̸̛s͘ w͘͢ḩ̵a҉̡͢t kinh hoàng nằm trong bóng tối tâm của Unicode ͞
ObscureRobot

@ObscureRobot Tôi thực sự muốn biết liệu những biểu tượng bổ sung đó có thể có trạng thái hay không
eonil

1
@Eonil - Tôi không chắc trạng thái có nghĩa là gì trong bối cảnh unicode.
ObscureRobot

@ObscureRobot Ví dụ: một số điểm mã như thế này: (begin curved line) (char1) (char2) … (charN) (end curved line)thay vì thế này : (curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2). Nói cách khác, đơn vị tối thiểu có thể được đưa ra?
eonil

2
Nghe có vẻ như một câu hỏi hay.
ObscureRobot

Câu trả lời:


181

Mọi thứ bạn chưa bao giờ muốn biết về Chuẩn hóa Unicode

Chuẩn hóa Canonical

Unicode bao gồm nhiều cách để mã hóa một số ký tự, đáng chú ý nhất là các ký tự có dấu. Chuẩn hóa Canonical thay đổi các điểm mã thành một dạng mã hóa chính tắc. Các điểm mã kết quả sẽ xuất hiện giống hệt với các điểm ban đầu chặn mọi lỗi trong phông chữ hoặc công cụ kết xuất.

Khi nào nên sử dụng

Vì các kết quả có vẻ giống nhau, nên luôn an toàn khi áp dụng chuẩn hóa chính tắc cho chuỗi trước khi lưu trữ hoặc hiển thị chuỗi đó, miễn là bạn có thể chịu đựng được kết quả không bị bit giống với đầu vào.

Chuẩn hóa Canonical có hai dạng: NFD và NFC. Hai là tương đương theo nghĩa là người ta có thể chuyển đổi giữa hai hình thức này mà không mất. So sánh hai chuỗi dưới NFC sẽ luôn cho kết quả giống như so sánh chúng dưới NFD.

NST

NFD có các nhân vật được mở rộng hoàn toàn. Đây là hình thức chuẩn hóa nhanh hơn để tính toán, nhưng kết quả là có nhiều điểm mã hơn (nghĩa là sử dụng nhiều không gian hơn).

Nếu bạn chỉ muốn so sánh hai chuỗi chưa được chuẩn hóa, thì đây là hình thức chuẩn hóa ưa thích trừ khi bạn biết bạn cần chuẩn hóa tương thích.

NFC

NFC kết hợp lại các điểm mã khi có thể sau khi chạy thuật toán NFD. Điều này mất nhiều thời gian hơn một chút, nhưng kết quả là chuỗi ngắn hơn.

Chuẩn hóa tương thích

Unicode cũng bao gồm nhiều ký tự không thực sự thuộc về nhưng được sử dụng trong các bộ ký tự cũ. Unicode đã thêm những thứ này để cho phép văn bản trong các bộ ký tự đó được xử lý dưới dạng Unicode, và sau đó được chuyển đổi lại mà không bị mất.

Chuẩn hóa tương thích chuyển đổi các chuỗi này thành chuỗi ký tự "thực" tương ứng và cũng thực hiện chuẩn hóa chính tắc. Các kết quả của chuẩn hóa tương thích có thể không xuất hiện giống hệt với bản gốc.

Các ký tự bao gồm thông tin định dạng được thay thế bằng các ký tự không. Ví dụ, nhân vật được chuyển đổi thành 9. Những người khác không liên quan đến sự khác biệt định dạng. Ví dụ, ký tự chữ số La Mã được chuyển đổi thành các chữ cái thông thường IX.

Rõ ràng, một khi chuyển đổi này đã được thực hiện, không còn có thể chuyển đổi một cách dễ dàng trở lại bộ ký tự gốc.

Khi nào sử dụng

Unicode Consortium cho thấy suy nghĩ về chuẩn hóa tương thích giống như một ToUpperCasebiến đổi. Nó là một cái gì đó có thể hữu ích trong một số trường hợp, nhưng bạn không nên chỉ áp dụng nó willy-nilly.

Một trường hợp sử dụng tuyệt vời sẽ là một công cụ tìm kiếm vì bạn có thể muốn tìm kiếm cho 9phù hợp .

Một điều có lẽ bạn không nên làm là hiển thị kết quả của việc áp dụng chuẩn hóa tương thích cho người dùng.

NFKC / NFKD

Hình thức chuẩn hóa tương thích có hai dạng NFKD và NFKC. Chúng có cùng mối quan hệ như giữa NFD và C.

Bất kỳ chuỗi nào trong NFKC vốn dĩ cũng có trong NFC, và tương tự đối với NFKD và NFD. Do đó NFKD(x)=NFD(NFKC(x)), và NFKC(x)=NFC(NFKD(x)), v.v.

Phần kết luận

Nếu nghi ngờ, hãy đi với chuẩn hóa chính tắc. Chọn NFC hoặc NFD dựa trên sự đánh đổi không gian / tốc độ áp dụng, hoặc dựa trên những gì được yêu cầu bởi thứ gì đó mà bạn đang hoạt động cùng.


42
Một tài liệu tham khảo nhanh để nhớ những gì các từ viết tắt đại diện cho: NF = dạng chuẩn hóa D = decompose (giải nén) , C = compose (nén) K = tương thích (vì "C" đã được sử dụng).
Mike Spross

12
Bạn luôn muốn NFD tất cả các chuỗi trên đầu vào là điều đầu tiên và NFC tất cả các chuỗi đầu ra là điều cuối cùng. Điều này là nổi tiếng.
tchrist

3
@tchrist: Đó thường là lời khuyên tốt, ngoại trừ trong những trường hợp hiếm hoi mà bạn muốn đầu ra là byte cho byte giống với đầu vào khi không có thay đổi nào được thực hiện. Có một số trường hợp khác mà bạn muốn NFC trong bộ nhớ hoặc NFD trên đĩa, nhưng chúng là ngoại lệ chứ không phải là quy tắc.
Kevin Cathcart

@Kevin: Có, NFD trong và NFC ra sẽ phá hủy các singletons. Tôi không chắc chắn rằng bất cứ ai quan tâm đến những điều đó, nhưng có thể.
tchrist

2
Bạn có thể nghĩ như vậy, nhưng từ phụ lục: "Để chuyển đổi chuỗi Unicode thành Biểu mẫu chuẩn hóa Unicode nhất định, bước đầu tiên là phân tách hoàn toàn chuỗi". Do đó, ngay cả khi chúng tôi chạy NFC, Q-Caron trước tiên sẽ trở thành Q + Caron và không thể soạn lại, vì các quy tắc ổn định cấm thêm ánh xạ thành phần mới. NFC được định nghĩa một cách hiệu quả là NFC(x)=Recompose(NFD(x)).
Kevin Cathcart

40

Một số ký tự, ví dụ, một chữ cái có dấu (giả sử é) có thể được biểu diễn theo hai cách - một điểm mã U+00E9hoặc chữ cái đơn giản theo sau là dấu trọng âm kết hợp U+0065 U+0301. Chuẩn hóa thông thường sẽ chọn một trong số này để luôn đại diện cho nó (điểm mã duy nhất cho NFC, hình thức kết hợp cho NFD).

Đối với các ký tự có thể được biểu thị bằng nhiều chuỗi ký tự cơ sở và dấu kết hợp (giả sử, "s, chấm bên dưới, chấm ở trên" so với đặt dấu chấm ở trên rồi chấm bên dưới hoặc sử dụng một ký tự cơ bản đã có một trong các dấu chấm), NFD sẽ cũng chọn một trong số này (dưới đây đi trước, như nó xảy ra)

Các phân tách tương thích bao gồm một số ký tự "không thực sự" là các ký tự nhưng là do chúng được sử dụng trong các bảng mã kế thừa. Chuẩn hóa thông thường sẽ không thống nhất các điều này (để duy trì tính toàn vẹn của chuyến đi khứ hồi - đây không phải là vấn đề đối với các hình thức kết hợp vì không có mã hóa kế thừa [ngoại trừ một số mã hóa tiếng Việt] được sử dụng cả hai), nhưng sẽ chuẩn hóa khả năng tương thích. Hãy suy nghĩ như ký hiệu kilogam "kg" xuất hiện trong một số bảng mã Đông Á (hoặc katakana nửa băng thông / toàn băng thông) và bảng chữ cái "fi" trong MacRoman.

Xem http://unicode.org/reports/tr15/ để biết thêm chi tiết.


1
Đây thực sự là câu trả lời chính xác. Nếu bạn chỉ sử dụng chuẩn hóa chính tắc trên văn bản có nguồn gốc trong một số bộ ký tự cũ, kết quả có thể được chuyển đổi trở lại thành bộ ký tự đó mà không bị mất. Nếu bạn sử dụng phân tách tương thích, bạn sẽ không có bất kỳ ký tự tương thích nào, nhưng không thể chuyển đổi trở lại bộ ký tự gốc mà không bị mất.
Kevin Cathcart

13

Các hình thức thông thường (của Unicode, không phải cơ sở dữ liệu) xử lý chủ yếu (độc quyền?) Với các ký tự có dấu phụ. Unicode cung cấp một số ký tự có dấu phụ "tích hợp", chẳng hạn như U + 00C0, "Latin Capital A with Grave". Có thể tạo cùng một ký tự từ `Latin Capital A" (U + 0041) với "Accent Grave Accent" (U + 0300). Điều đó có nghĩa là mặc dù hai chuỗi tạo ra cùng một ký tự, một byte so sánh sẽ cho thấy họ là hoàn toàn khác nhau.

Bình thường hóa là một nỗ lực để đối phó với điều đó. Bình thường hóa đảm bảo (hoặc ít nhất là cố gắng) rằng tất cả các ký tự được mã hóa theo cùng một cách - tất cả đều sử dụng dấu phụ kết hợp riêng khi cần hoặc tất cả sử dụng một điểm mã duy nhất bất cứ khi nào có thể. Từ quan điểm so sánh, thực sự không có vấn đề gì với nhiều thứ bạn chọn - gần như bất kỳ chuỗi chuẩn hóa nào cũng sẽ so sánh đúng với một chuỗi chuẩn hóa khác.

Trong trường hợp này, "khả năng tương thích" có nghĩa là khả năng tương thích với mã giả định rằng một điểm mã bằng một ký tự. Nếu bạn có mã như vậy, có lẽ bạn muốn sử dụng biểu mẫu tương thích thông thường. Mặc dù tôi chưa bao giờ thấy nó được nêu trực tiếp, tên của các hình thức bình thường ngụ ý rằng tập đoàn Unicode cho rằng nên sử dụng các dấu phụ kết hợp riêng biệt. Điều này đòi hỏi nhiều trí thông minh hơn để đếm các ký tự thực tế trong một chuỗi (cũng như những thứ như phá vỡ chuỗi một cách thông minh), nhưng linh hoạt hơn.

Nếu bạn đang sử dụng đầy đủ ICU, rất có thể bạn muốn sử dụng mẫu thông thường chính tắc. Nếu bạn đang cố gắng tự viết mã mà (ví dụ) giả sử một điểm mã bằng với một ký tự, thì có lẽ bạn muốn dạng tương thích thông thường làm cho điều đó càng thường xuyên càng tốt.


Vì vậy, đây là phần mà các Hàm Grapheme xuất hiện sau đó. Không chỉ là ký tự nhiều byte hơn ASCII - mà nhiều chuỗi có thể là một ký tự phải không? (Trái ngược với các chức năng chuỗi MB .)
Xeoncross

4
Không, 'một điểm mã là một ký tự' tương ứng với NFC (điểm có dấu kết hợp là NFD và không có điểm nào là "tương thích") - Các chuẩn hóa tương thích NFKC / NFKD là một vấn đề khác nhau; khả năng tương thích (hoặc thiếu) đối với các bảng mã kế thừa, ví dụ như có các ký tự riêng biệt cho tiếng Hy Lạp mu và 'micro' (đó là một điều thú vị để đưa ra vì phiên bản "tương thích" là phiên bản trong khối Latin 1)
Random832

@ Random832: Rất tiếc, hoàn toàn đúng. Tôi nên biết rõ hơn là đi từ bộ nhớ khi tôi không làm việc với nó trong một hoặc hai năm qua.
Jerry Coffin

@ Random832 Điều đó không đúng. Bạn có thể nói về vấn đề này. Hãy xem xét hai biểu đồ, ̲̲̃̃ và. Có rất nhiều cách để viết từng cái, trong đó chính xác một trong số đó là NFC và một NFD, nhưng những cách khác cũng tồn tại. Không có trường hợp nào chỉ có một điểm mã. NFD cho lần đầu tiên là "o\x{332}\x{303}\x{304}", và NFC là "\x{22D}\x{332}". Đối với NFD thứ hai là "o\x{332}\x{304}\x{303}"và NFC là "\x{14D}\x{332}\x{303}". Tuy nhiên, nhiều khả năng phi kinh điển tồn tại tương đương với những khả năng này. Chuẩn hóa cho phép so sánh nhị phân của đồ thị tương đương kinh điển.
tchrist

5

Nếu hai chuỗi unicode tương đương về mặt kinh điển thì các chuỗi thực sự giống nhau, chỉ sử dụng các chuỗi unicode khác nhau. Ví dụ có thể được biểu diễn bằng cách sử dụng ký tự hoặc kết hợp A và.

Nếu các chuỗi chỉ tương thích tương đương thì các chuỗi không nhất thiết phải giống nhau, nhưng chúng có thể giống nhau trong một số ngữ cảnh. Ví dụ có thể được coi là tương tự như ff.

Vì vậy, nếu bạn đang so sánh các chuỗi, bạn nên sử dụng tương đương chính tắc, bởi vì tương đương tương thích không phải là tương đương thực sự.

Nhưng nếu bạn muốn sắp xếp một tập hợp các chuỗi, có thể có ý nghĩa khi sử dụng tính tương đương vì nó gần giống nhau.


5

Điều này thực sự khá đơn giản. UTF-8 thực sự có một số đại diện khác nhau của cùng một "nhân vật". (Tôi sử dụng ký tự trong dấu ngoặc kép vì byte khôn ngoan, chúng khác nhau, nhưng thực tế chúng giống nhau). Một ví dụ được đưa ra trong tài liệu liên kết.

Ký tự "" có thể được biểu diễn dưới dạng chuỗi byte 0xc387. Nhưng nó cũng có thể được biểu diễn bằng một C(0x43) theo sau là chuỗi byte 0xcca7. Vì vậy, bạn có thể nói rằng 0xc387 và 0x43cca7 là cùng một nhân vật. Lý do hoạt động, là 0xcca7 là một dấu hiệu kết hợp; điều đó có nghĩa là nó lấy ký tự trước nó (a Cở đây) và sửa đổi nó.

Bây giờ, về sự khác biệt giữa tương đương chính tắc và tương đương tương thích, chúng ta cần xem xét các ký tự nói chung.

Có 2 loại ký tự, những loại truyền đạt ý nghĩa thông qua giá trị và những loại lấy ký tự khác và thay đổi nó. 9 là một nhân vật có ý nghĩa. Một siêu tập lệnh mang ý nghĩa đó và thay đổi nó bằng cách trình bày. Vì vậy, theo kinh điển, chúng có ý nghĩa khác nhau, nhưng chúng vẫn đại diện cho nhân vật cơ sở.

Tương đương Canonical là nơi chuỗi byte được biểu hiện cùng một ký tự có cùng ý nghĩa. Tương đương tương thích là khi chuỗi byte hiển thị một ký tự khác có cùng ý nghĩa cơ sở (mặc dù nó có thể bị thay đổi). 9 và tương đương với nhau vì cả hai đều có nghĩa là "9", nhưng không tương đương về mặt kinh điển vì chúng không có cùng đại diện.


@tchrist: Đọc lại câu trả lời. Tôi thậm chí không bao giờ đề cập đến các cách khác nhau để đại diện cho cùng một điểm mã. Tôi đã nói có nhiều cách để thể hiện cùng một ký tự được in (thông qua các tổ hợp và nhiều ký tự). Áp dụng cho cả UTF-8 và Unicode. Vì vậy, downvote và bình luận của bạn không thực sự áp dụng cho tất cả những gì tôi nói. Trên thực tế, về cơ bản, tôi đã thực hiện cùng một điểm mà poster hàng đầu ở đây đã tạo ra (mặc dù không tốt) ...
ircmaxell

4

Việc tương đương kinh điển hay tương đương tương thích có liên quan hơn đến bạn hay không tùy thuộc vào ứng dụng của bạn. Cách suy nghĩ của ASCII về so sánh chuỗi gần như ánh xạ tới sự tương đương kinh điển, nhưng Unicode đại diện cho rất nhiều ngôn ngữ. Tôi không nghĩ an toàn khi cho rằng Unicode mã hóa tất cả các ngôn ngữ theo cách cho phép bạn đối xử với chúng giống như ASCII của Tây Âu.

Hình 1 và 2 cung cấp các ví dụ tốt về hai loại tương đương. Theo tính tương đương, có vẻ như cùng một số ở dạng phụ và siêu tập lệnh sẽ so sánh bằng nhau. Nhưng tôi không chắc rằng giải quyết vấn đề tương tự như hình thức ả rập hay các ký tự xoay.

Sự thật phũ phàng của xử lý văn bản Unicode là bạn phải suy nghĩ sâu sắc về các yêu cầu xử lý văn bản của ứng dụng, sau đó giải quyết chúng cũng như với các công cụ có sẵn. Điều đó không trực tiếp giải quyết câu hỏi của bạn, nhưng một câu trả lời chi tiết hơn sẽ yêu cầu các chuyên gia ngôn ngữ cho từng ngôn ngữ bạn muốn hỗ trợ.


1

Vấn đề so sánh các chuỗi : hai chuỗi có nội dung tương đương với mục đích của hầu hết các ứng dụng có thể chứa các chuỗi ký tự khác nhau.

Xem tính tương đương chính tắc của Unicode : nếu thuật toán so sánh đơn giản (hoặc phải nhanh), thì tương đương Unicode không được thực hiện. Ví dụ, sự cố này xảy ra trong so sánh chính tắc XML, xem http://www.w3.org/TR/xml-c14n

Để tránh vấn đề này ... Sử dụng tiêu chuẩn nào? "UTF8 mở rộng" hay "UTF8 nhỏ gọn"?
Sử dụng "ç" hoặc "c + ◌̧."?

W3C và những người khác (ví dụ tên tệp ) đề nghị sử dụng "được soạn thảo theo quy tắc" (ghi nhớ C về các chuỗi ngắn hơn "nhỏ gọn nhất") ... Vì vậy,

Tiêu chuẩn là C ! nghi ngờ sử dụng NFC

Đối với khả năng tương tác và cho các lựa chọn "quy ước về cấu hình" , khuyến nghị là sử dụng NFC , để "chuẩn hóa" các chuỗi bên ngoài. Ví dụ, để lưu trữ XML chuẩn, lưu trữ nó trong "FORM_C". CSV của W3C trên Nhóm làm việc Web cũng giới thiệu NFC (phần 7.2).

PS: de "FORM_C" là hình thức mặc định trong hầu hết các thư viện. Ví dụ. trong trình chuẩn hóa của PHP.isn normalized () .


Có thuật ngữ " dạng compostion " ( FORM_C) được sử dụng cho cả hai, để nói rằng "một chuỗi ở dạng C-canonical" (kết quả của phép biến đổi NFC) và để nói rằng thuật toán biến đổi được sử dụng ... Xem http: //www.macchiato.com/unicode/nfc-faq

(...) Mỗi ​​chuỗi sau đây (hai chuỗi đầu tiên là chuỗi ký tự đơn) đại diện cho cùng một ký tự:

  1. U + 00C5 (Å) LATIN VỐN THƯỞNG A VỚI RING TRÊN
  2. U + 212B (Å) DẤU HIỆU ANGSTROM
  3. U + 0041 (A) LATIN VỐN THƯỞNG A + U + 030A () KẾT HỢP RING TRÊN

Các trình tự này được gọi là tương đương kinh điển. Các hình thức đầu tiên trong số này được gọi là NFC - cho Mẫu bình thường hóa C, trong đó C là để phân hủy . (...) Một chức năng chuyển đổi một chuỗi S thành dạng NFC có thể được viết tắt là toNFC(S), trong khi một chức năng kiểm tra xem S có trong NFC được viết tắt là isNFC(S).


Lưu ý: để kiểm tra chuẩn hóa các chuỗi nhỏ (tham chiếu thực thể UTF-8 hoặc XML), bạn có thể sử dụng kiểm tra / chuẩn hóa trình chuyển đổi trực tuyến này .


Tôi bối rối. Tôi đã đi đến trang thử nghiệm trực tuyến này và tôi nhập vào đó: "TST MÉ pleasé." và thử tất cả 4 chuẩn hóa đã cho - không thay đổi văn bản của tôi theo bất kỳ cách nào, ngoại trừ việc nó thay đổi các mã được sử dụng để trình bày các ký tự đó. Có phải tôi đã nghĩ sai rằng "bình thường hóa" có nghĩa là "loại bỏ tất cả các dấu phụ và tương tự", và nó thực sự có nghĩa là - chỉ cần thay đổi mã hóa utf bên dưới?
dùng

Xin chào @userfuser có lẽ bạn cần một vị trí, về ứng dụng: là so sánh hoặc chuẩn hóa văn bản của bạn? Bài viết của tôi ở đây chỉ là về "để chuẩn hóa" các ứng dụng. PS: khi tất cả thế giới sử dụng tiêu chuẩn, vấn đề so sánh sẽ biến mất.
Peter Krauss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.