UTF-16 có chiều rộng cố định hay chiều rộng thay đổi? Tại sao UTF-8 không có vấn đề về thứ tự byte?


16
  1. UTF-16 có chiều rộng cố định hay chiều rộng thay đổi? Tôi nhận được kết quả khác nhau từ các nguồn khác nhau:

    Từ http://www.tbray.org/ongceed/When/200x/2003/04/26/UTF :

    UTF-16 lưu trữ các ký tự Unicode trong các đoạn mười sáu bit.

    Từ http://en.wikipedia.org/wiki/UTF-16/UCS-2 :

    UTF-16 (Định dạng chuyển đổi Unicode 16 bit) là mã hóa ký tự cho Unicode có khả năng mã hóa 1.112.064 [1] số (được gọi là điểm mã) trong không gian mã Unicode từ 0 đến 0x10FFFF. Nó tạo ra kết quả có độ dài thay đổi của một hoặc hai đơn vị mã 16 bit cho mỗi điểm mã.

  2. Từ nguồn đầu tiên

    UTF-8 cũng có lợi thế là đơn vị mã hóa là byte, do đó không có vấn đề về thứ tự byte.

    Tại sao UTF-8 không có vấn đề về thứ tự byte? Nó có độ rộng thay đổi và một ký tự có thể chứa nhiều hơn một byte, vì vậy tôi nghĩ thứ tự byte vẫn có thể là một vấn đề?

Cảm ơn và trân trọng!


Bài viết tuyệt vời này Tối thiểu tuyệt đối Mỗi nhà phát triển phần mềm Tuyệt đối, Tích cực phải biết về Unicode và Bộ ký tự (Không có lý do!) Sẽ giúp trả lời tất cả các câu hỏi của bạn về Unicode và UTF ..
Sorceror

Câu trả lời:


13

(1) Chuỗi byte có nghĩa là gì, một mảng char trong C? UTF-16 là một chuỗi byte, hay nó là gì? (2) Tại sao một chuỗi byte không liên quan gì đến độ dài thay đổi?

Bạn dường như đang hiểu nhầm vấn đề endian là gì. Đây là một bản tóm tắt ngắn gọn.

Một số nguyên 32 bit chiếm 4 byte. Bây giờ, chúng ta biết thứ tự hợp lý của các byte này. Nếu bạn có số nguyên 32 bit, bạn có thể nhận được byte cao này bằng mã sau:

uint32_t value = 0x8100FF32;
uint8_t highByte = (uint8_t)((value >> 24) & 0xFF); //Now contains 0x81

Đó là tất cả tốt và tốt. Vấn đề bắt đầu là cách thức lưu trữ phần cứng và lấy số nguyên từ bộ nhớ.

Theo thứ tự Big Endian, một đoạn bộ nhớ 4 byte mà bạn đọc dưới dạng số nguyên 32 bit sẽ được đọc với byte đầu tiên là byte cao:

[0][1][2][3]

Theo thứ tự Little Endian, một đoạn bộ nhớ 4 byte mà bạn đọc dưới dạng số nguyên 32 bit sẽ được đọc với byte đầu tiên là byte thấp :

[3][2][1][0]

Nếu bạn có một con trỏ tới một con trỏ tới giá trị 32 bit, bạn có thể làm điều này:

uint32_t value = 0x8100FF32;
uint32_t *pValue = &value;
uint8_t *pHighByte = (uint8_t*)pValue;
uint8_t highByte = pHighByte[0]; //Now contains... ?

Theo C / C ++, kết quả của điều này là không xác định. Nó có thể là 0x81. Hoặc nó có thể là 0x32. Về mặt kỹ thuật, nó có thể trả về bất cứ thứ gì, nhưng đối với các hệ thống thực, nó sẽ trả về cái này hay cái khác.

Nếu bạn có một con trỏ tới một địa chỉ bộ nhớ, bạn có thể đọc địa chỉ đó dưới dạng giá trị 32 bit, giá trị 16 bit hoặc giá trị 8 bit. Trên một máy endian lớn, con trỏ trỏ đến byte cao; trên một máy endian nhỏ, con trỏ trỏ tới byte thấp.

Lưu ý rằng đây là tất cả về đọc và ghi vào / từ bộ nhớ. Nó không có gì để làm với mã C / C ++ nội bộ. Phiên bản đầu tiên của mã, phiên bản mà C / C ++ không khai báo là không xác định, sẽ luôn hoạt động để có được byte cao.

Vấn đề là khi bạn bắt đầu đọc các luồng byte. Chẳng hạn như từ một tập tin.

Các giá trị 16 bit có cùng các vấn đề như các giá trị 32 bit; chúng chỉ có 2 byte thay vì 4. Do đó, một tệp có thể chứa các giá trị 16 bit được lưu trữ theo thứ tự endian lớn hoặc endian nhỏ.

UTF-16 được định nghĩa là một chuỗi các giá trị 16 bit . Hiệu quả, nó là một uint16_t[]. Mỗi đơn vị mã riêng lẻ là một giá trị 16 bit. Do đó, để tải UTF-16 đúng cách, bạn phải biết tính chất cuối cùng của dữ liệu là gì.

UTF-8 được định nghĩa là một chuỗi các giá trị 8 bit . Nó là một uint8_t[]. Mỗi đơn vị mã riêng lẻ có kích thước 8 bit: một byte đơn.

Bây giờ, cả UTF-16 và UTF-8 đều cho phép nhiều đơn vị mã (giá trị 16 bit hoặc 8 bit) kết hợp với nhau để tạo thành một bảng mã Unicode (một "ký tự", nhưng đó không phải là thuật ngữ chính xác; ). Thứ tự của các đơn vị mã này tạo thành một mật mã được quyết định bởi các bảng mã UTF-16 và UTF-8.

Khi xử lý UTF-16, bạn đọc giá trị 16 bit, thực hiện bất kỳ chuyển đổi endian nào là cần thiết. Sau đó, bạn phát hiện nếu đó là một cặp thay thế; nếu có, thì bạn đọc một giá trị 16 bit khác, kết hợp cả hai và từ đó, bạn nhận được giá trị mã điểm Unicode.

Khi xử lý UTF-8, bạn đọc giá trị 8 bit. Không thể chuyển đổi endian vì chỉ có một byte. Nếu byte đầu tiên biểu thị một chuỗi nhiều byte, thì bạn đọc một số lượng byte, như được chỉ định bởi chuỗi nhiều byte. Mỗi byte riêng lẻ là một byte và do đó không có chuyển đổi về cuối. Thứ tự của các byte này trong chuỗi, giống như thứ tự của các cặp thay thế trong UTF-16, được xác định bởi UTF-8.

Vì vậy, không thể có vấn đề về endian với UTF-8.


10

Câu trả lời của Jeremy Banks là chính xác theo như nó đi, nhưng không giải quyết thứ tự byte.

Khi bạn sử dụng UTF-16, hầu hết các glyph được lưu trữ bằng một từ hai byte - nhưng khi từ đó được lưu trữ trong một tệp đĩa, bạn sử dụng thứ tự nào để lưu trữ các byte cấu thành?

Lấy ví dụ, glyph của CJK (tiếng Trung) cho từ "nước" có mã hóa UTF-16 ở hệ thập lục phân là 6C34. Khi bạn viết rằng hai byte vào đĩa, bạn có viết nó là "big-endian" (hai byte là 6C 34) không? Hay bạn viết nó là "little endian (hai byte là 34 6C)?

Với UTF-16, cả hai thứ tự đều hợp pháp và bạn thường chỉ ra tệp nào có bằng cách tạo từ đầu tiên trong tệp thành Dấu thứ tự Byte (BOM), để mã hóa cuối lớn là FE FF và cho end-endian mã hóa là FF FE.

UTF-32 có cùng một vấn đề và cùng một giải pháp.

UTF-8 không có vấn đề này, bởi vì nó có độ dài thay đổi và bạn thực sự viết một chuỗi byte của glyph như thể nó là endian nhỏ. Chẳng hạn, chữ "P" luôn được mã hóa bằng một byte - 80 - và ký tự thay thế luôn được mã hóa bằng hai byte FF FD theo thứ tự đó.

Một số chương trình đặt chỉ báo ba byte (EF BB BF) khi bắt đầu tệp UTF-8 và giúp phân biệt UTF-8 với các mã hóa tương tự như ASCII, nhưng không phổ biến lắm ngoại trừ trên MS Windows.


Cảm ơn! (1) chữ "P" chỉ là một byte trong UTF-8. Tại sao ký tự thay thế được thêm vào mã của nó? (2) Trong UTF-8, có các ký tự khác có nhiều hơn một byte trong UTF-8. Tại sao thứ tự byte giữa các byte cho mỗi ký tự như vậy không phải là vấn đề?
Tim

@Tim: (1) Bạn không thêm ký tự thay thế vào mã cho P. Nếu bạn thấy 80 FF FD, đó là hai ký tự - ký tự P và ký tự thay thế.
Bob Murphy

(2) Bạn luôn viết và đọc hai byte cho "ký tự thay thế" là FF FD, theo thứ tự đó. Sẽ chỉ có một vấn đề theo thứ tự byte nếu bạn cũng có thể viết "ký tự thay thế" là FD FF - nhưng bạn không thể; chuỗi hai byte đó sẽ là một cái gì đó khác với "ký tự thay thế".
Bob Murphy

1
@Tim: Bạn có thể muốn làm việc thông qua en.wikipedia.org/wiki/UTF-8 . Nó thực sự khá tốt và nếu bạn có thể hiểu tất cả về nó và các trang Wikipedia liên quan đến Unicode khác, tôi nghĩ bạn sẽ thấy bạn không còn câu hỏi nào nữa.
Bob Murphy

4
Lý do UTF-8 không có vấn đề với thứ tự byte là vì mã hóa được định nghĩa là một chuỗi byte và không có biến thể với độ bền khác nhau. Nó không có gì để làm với chiều dài thay đổi.
starblue
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.