$ locale charmap
UTF-8
Trong môi trường hiện tại của tôi, bộ ký tự là UTF-8, nghĩa là, các ký tự được mã hóa từ 1 đến 4 byte cho mỗi ký tự (mặc dù vì định nghĩa ban đầu của UTF-8 cho phép mã ký tự lên đến 0x7fffffff, hầu hết các công cụ sẽ nhận ra UTF- Chuỗi 8 byte lên đến 6 byte).
Trong bộ ký tự đó, tất cả các ký tự từ Unicode đều có sẵn, a a
được mã hóa thành giá trị byte 65, a 乕
là 3 byte 228 185 149 và é
như hai chuỗi 195 169 chẳng hạn.
$ printf 乕 | wc -mc
1 3
$ printf a | wc -mc
1 1
Hiện nay:
$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15
Tôi đã sửa đổi môi trường của mình, nơi bộ ký tự hiện là ISO-8859-15 (những thứ khác như ngôn ngữ, ký hiệu tiền tệ, định dạng ngày cũng đã được sửa đổi, bộ sưu tập các cài đặt khu vực đó được gọi là miền địa phương ). Tôi cần bắt đầu một trình giả lập thiết bị đầu cuối mới trong môi trường đó để nó điều chỉnh kết xuất ký tự của nó với miền địa phương mới.
ISO-8859-15 là một bộ ký tự byte đơn, có nghĩa là nó chỉ có 256 ký tự (thực tế thậm chí còn ít hơn số lượng thực sự được bao phủ). Bộ ký tự cụ thể đó được sử dụng cho các ngôn ngữ của Tây Âu vì nó bao gồm hầu hết các ngôn ngữ của nó (và biểu tượng đồng euro).
Nó có a
ký tự có giá trị byte 65 như trong UTF-8 hoặc ASCII, nó cũng có é
ký tự (ví dụ như thường được sử dụng trong tiếng Pháp hoặc tiếng Tây Ban Nha) nhưng với giá trị byte 233, nó không có ký tự.
Trong môi trường đó, wc -c
và wc -m
sẽ luôn cho kết quả tương tự.
Trong Ubuntu giống như trên hầu hết các hệ thống giống Unix hiện đại, mặc định thường là UTF-8 vì đây là bộ ký tự được hỗ trợ (và mã hóa) duy nhất bao trùm toàn bộ phạm vi Unicode.
Các mã hóa ký tự nhiều byte khác tồn tại, nhưng chúng không được hỗ trợ tốt trên Ubuntu và bạn phải trải qua các vòng để có thể tạo một miền địa phương với chúng, và nếu bạn làm như vậy, bạn sẽ thấy rằng nhiều thứ không làm việc đúng cách
Vì vậy, có hiệu lực trên Ubuntu, các bộ ký tự là một byte hoặc UTF-8.
Bây giờ, một vài ghi chú:
Trong UTF-8, không phải tất cả các chuỗi byte tạo thành các ký tự hợp lệ. Chẳng hạn, tất cả các ký tự UTF-8 không phải là ký tự ASCII được tạo thành với các byte đều có tập bit thứ 8, nhưng chỉ có ký tự đầu tiên có bộ bit thứ 7.
Nếu bạn có một chuỗi byte với tập bit thứ 8, không có chuỗi nào có tập bit thứ 7, thì điều đó không thể được dịch thành ký tự. Và đó là khi bạn bắt đầu gặp vấn đề và không nhất quán vì phần mềm không biết phải làm gì với những điều đó. Ví dụ:
$ printf '\200\200\200' | wc -mc
0 3
$ printf '\200\200\200' | grep -q . || echo no
no
wc
và grep
không tìm thấy nhân vật nào trong đó nhưng:
$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3
bash
tìm thấy 3. Khi nó không thể ánh xạ một chuỗi byte thành một ký tự, nó sẽ xem xét mỗi byte một ký tự.
Nó thậm chí còn phức tạp hơn khi có các điểm mã trong Unicode không hợp lệ dưới dạng ký tự và một số không phải là ký tự và tùy thuộc vào công cụ, mã hóa UTF-8 của chúng có thể hoặc không được coi là ký tự.
Một điều khác cần xem xét là sự khác biệt giữa nhân vật và đồ thị, và cách chúng được hiển thị.
$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
3 6
Ở đó, chúng tôi đã mã 3 ký tự là 6 byte được hiển thị dưới dạng một biểu đồ, bởi vì chúng tôi có 3 ký tự được kết hợp với nhau (một ký tự cơ sở, dấu trọng âm kết hợp và vòng tròn kết hợp).
Việc triển khai GNU wc
như tìm thấy trên Ubuntu có một -L
công tắc để cho bạn biết chiều rộng hiển thị của dòng rộng nhất trong đầu vào:
$ printf 'e\u301\u20dd\n' | wc -L
1
Bạn cũng sẽ thấy rằng một số ký tự chiếm 2 ô trong phép tính chiều rộng đó giống như 乕
ký tự của chúng ta ở trên:
$ echo 乕 | wc -L
2
Tóm lại: trong từ hoang dã hơn, byte, ký tự và đồ thị không nhất thiết phải giống nhau.