Làm cách nào để chuyển đổi biểu tượng cảm xúc được chỉ định bởi mã U + xxxxx sang utf-8?


16

Biểu tượng cảm xúc dường như được chỉ định bằng cách sử dụng định dạng U + xxxxx
trong đó mỗi x là một chữ số thập lục phân.

Ví dụ: U + 1F615mã Unicode Consortium chính thức cho "khuôn mặt bối rối"

Như tôi thường bối rối, tôi có một mối quan hệ mạnh mẽ với biểu tượng này.

Đại diện U + 1F615 gây nhầm lẫn cho tôi vì tôi nghĩ rằng mã hóa duy nhất có thể cho các ký tự unicode cần 8, 16, 24 hoặc 32 bit, trong khi 5 chữ số hex yêu cầu 5x4 = 20 bit.

Tôi đã phát hiện ra rằng biểu tượng này dường như được biểu thị bằng một chuỗi hex hoàn toàn khác trong bash:

$echo -n 😕 | hexdump
0000000 f0 9f 98 95                                    
0000004

$echo -e "\xf0\x9f\x98\x95"
😕

$PS1=$'\xf0\x9f\x98\x95  >'
😕  >

Tôi đã mong đợi U + 1F615 chuyển đổi thành thứ gì đó như \ x00 \ x01 \ xF6 \ x15 .

Tôi không thấy mối quan hệ giữa 2 bảng mã này?

Khi tôi tìm kiếm một biểu tượng trong danh sách Hiệp hội Unicode chính thức , tôi muốn có thể sử dụng mã đó trực tiếp mà không phải chuyển đổi thủ công theo cách tẻ nhạt này. I E

  • tìm biểu tượng trên một số trang web
  • sao chép nó vào clipboard của trình duyệt web
  • dán nó trong bash để lặp lại thông qua hexdump để khám phá mã REAL.

Tôi có thể sử dụng mã 20 bit này để xác định mã 32 bit là gì không?

Có một mối quan hệ tồn tại giữa 2 số này?

Câu trả lời:


20

UTF-8là một mã hóa độ dài thay đổi của Unicode. Nó được thiết kế để thay thế cho ASCII. Xem Wikipedia để biết chi tiết về mã hóa. \x00 \x01 \xF6 \x15sẽ được UCS-4BEhoặc UTF-32BEmã hóa.

Để lấy từ điểm mã Unicode sang mã hóa UTF-8, giả sử bùa chú của miền địa phương là UTF-8 (xem đầu ra của locale charmap), chỉ cần:

$ printf '\U1F615\n'
😕
$ echo -e '\U1F615'
😕
$ confused_face=$'\U1F615'

Cái sau sẽ có trong phiên bản tiếp theo của tiêu chuẩn POSIX .

AFAIK, cú pháp đã được giới thiệu vào năm 2000 bởi các độc GNU printftiện ích (như trái ngược với các printftiện ích của vỏ GNU), đưa đến echo/ printf/ $'...'builtins đầu tiên của zshnăm 2003 , ksh93 vào năm 2004, bash trong năm 2010 (mặc dù không hoạt động đúng có cho đến năm 2014 ), nhưng rõ ràng được truyền cảm hứng từ các ngôn ngữ khác.

ksh93cũng hỗ trợ nó như printf '\x1f615\n'printf '\u{1f615}\n'.

$'\uXXXX'$'\UXXXXXXXX'được hỗ trợ bởi zsh, bash, ksh93, mkshvà FreeBSD sh, GNU printf, GNU echo.

Một số yêu cầu tất cả các chữ số ( \U0001F615trái ngược với \U1F615) mặc dù điều đó có khả năng thay đổi trong các phiên bản trong tương lai vì POSIX sẽ cho phép ít chữ số hơn. Trong mọi trường hợp, bạn cần tất cả các chữ số nếu \UXXXXXXXXđược theo sau bởi các chữ số thập lục phân như trong \U0001F615FOX, như \U1F615FOXđã có $'\U001F615F'OX.

Một số mở rộng thành các ký tự trong mã hóa của miền địa phương hiện tại tại thời điểm chuỗi được phân tích cú pháp hoặc tại thời điểm nó được mở rộng, một số chỉ trong UTF-8 bất kể miền địa phương. Nếu ký tự không có sẵn trong mã hóa của miền địa phương hiện tại, hành vi sẽ thay đổi giữa các shell.

Vì vậy, để có tính di động tốt nhất, tốt nhất là chỉ sử dụng nó trong các ngôn ngữ UTF-8 và sử dụng tất cả các chữ số và sử dụng nó trong $'...':

printf '%s\n' $'\U0001F615'

Lưu ý rằng:

LC_ALL=C.UTF-8; printf '%s\n' $'\U0001F615'

hoặc là:

{
  LC_ALL=C.UTF-8
  printf '%s\n' $'\U0001F615'
}

Sẽ không hoạt động với tất cả các shell (bao gồm bash) vì $'\U0001F615'được phân tích cú pháp trước khi LC_ALLđược gán. (cũng lưu ý rằng không có gì đảm bảo rằng một hệ thống sẽ có một miền được gọi là C.UTF-8)

Bạn sẽ cần:

LC_ALL=C.UTF-8; eval "confused_face=$'\U0001F615'"

Hoặc là:

LC_ALL=C.UTF-8
printf '%s\n' $'\U0001F615'

(không nằm trong lệnh ghép hoặc hàm).


Ngược lại, để chuyển từ mã hóa UTF-8 sang điểm mã Unicode, hãy xem câu hỏi khác này hoặc câu hỏi kia .

$ unicode 😕 
U+1F615 CONFUSED FACE
UTF-8: f0 9f 98 95  UTF-16BE: d83dde15  Decimal: 😕
😕
Category: So (Symbol, Other)
Bidi: ON (Other Neutrals)

$ perl -CA -le 'printf "%x\n", ord shift' 😕
1f615

2
Lưu ý rằng nếu \U1F615được theo sau bởi một chữ số thập lục phân hợp lệ khác thì đó sẽ được coi là một phần của chuỗi thoát. Để làm cho nó hoạt động bất kể nó đi theo cái gì, nó phải có đủ các số 0 đứng đầu dài chính xác tám chữ số:\U0001F615
kasperd

@kasperd, cảm ơn. Vâng, đáng chú ý. Tôi đã bao gồm điều đó trong câu trả lời.
Stéphane Chazelas

7

Đây là một cách để chuyển đổi từ UTF-32 (endian lớn) sang UTF-8

$ confused=$(echo -ne "\x0\x01\xF6\x15" | iconv -f UTF-32BE -t UTF-8)     
$ echo $confused 
😕

Bạn sẽ nhận thấy giá trị hex của mình 0x01F615trong đó, được thêm 0 đầu để điền 32 bit.

Trang Wikipedia trên UTF-8 giải thích sự chuyển đổi từ một bảng mã Unicode sang biểu diễn UTF-8 của nó rất rõ ràng. Nhưng cố gắng tự làm nó trong kịch bản shell có thể không phải là ý tưởng tốt nhất.

UTF-32 có chiều rộng cố định và sự tương ứng giữa mã hóa và đại diện UTF-32 là tầm thường - giá trị là như nhau.


6

Cách hay để làm điều đó trong đầu hoặc trên giấy:

  1. Chỉ ra có bao nhiêu byte: các giá trị trong U + 0080 là một byte, khác với U + 0800 là 2 byte, khác với U + 10000 là 3 byte, khác 4 byte. Trong trường hợp của bạn, 4 byte.

  2. Chuyển đổi hex sang bát phân : 0373025.

  3. Bắt đầu từ cuối, bóc 2 chữ số bát phân tại một thời điểm để có được một chuỗi các giá trị bát phân : 037 030 025.

  4. Nếu bạn có ít giá trị bát phân hơn số byte dự kiến, hãy thêm 0 vào đầu : 000 037 030 025.

  5. Đối với tất cả nhưng đầu tiên, thêm vào 0200để có được : 000 0237 0230 0225.

  6. Đối với lần đầu tiên, hãy thêm 0300nếu độ dài dự kiến ​​là 2, 0340nếu là 3 hoặc 0360nếu là 4, để nhận : 360 0237 0230 0225.

Bây giờ viết như một chuỗi các bát phân thoát : \360\237\230\225. Tùy chọn chuyển đổi trở lại hex nếu bạn muốn.

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.