Trong bash, làm cách nào tôi có thể chuyển đổi một Codepoint Unicode [0-9A-F] thành một ký tự có thể in được?


23

Tôi có một danh sách các điểm mã Unicode, nhưng tôi không biết một cách "đơn giản" để chuyển đổi các giá trị hex này thành các ký tự thực tế mà chúng đại diện cho ...

Tôi đã nghe nói rằng zshecho -e '\u0965', nhưng tôi sử dụng bash 4.1.

Có một cái gì đó đơn giản như phương pháp zsh, cho bash?


Câu trả lời:


16

Bạn có thể sử dụng echo hoặc / bin / echo của bash từ coreutils GNU kết hợp với iconv:

echo -ne '\x09\x65' | iconv -f utf-16be

Theo mặc định iconv chuyển đổi sang mã hóa cục bộ của bạn. Có lẽ dễ mang theo hơn là dựa vào lệnh shell hoặc echo cụ thể là Perl. Hầu hết mọi hệ thống UNIX mà tôi biết đều có sẵn Perl và nó thậm chí còn có một số cổng Windows.

perl -C -e 'print chr 0x0965'

Hầu hết thời gian tôi cần làm điều này, tôi đang ở trong một trình soạn thảo như Vim / GVim có hỗ trợ tích hợp. Trong khi ở chế độ chèn, nhấn Ctrl-V theo sau là u, sau đó nhập bốn ký tự hex. Nếu bạn muốn một ký tự ngoài U + FFFF, hãy sử dụng chữ U viết hoa và nhập 8 ký tự hex. Vim cũng hỗ trợ tùy chỉnh dễ dàng để tạo keymap. Nó chuyển đổi một loạt các ký tự thành một biểu tượng khác. Ví dụ: tôi có một sơ đồ phím mà tôi đã phát triển được gọi là www, nó chuyển đổi TM thành ™, (C) thành ©, (R) thành ®, v.v. Tôi cũng có một sơ đồ cho Klingon khi điều đó trở nên cần thiết. Tôi chắc chắn Emacs có một cái gì đó tương tự. Nếu bạn đang ở trong ứng dụng GTK + bao gồm GVim và Gnome Terminal, bạn có thể thử Control-Shift-u theo sau là 4 ký tự hex để tạo ký tự Unicode. Tôi chắc chắn KDE / Qt có cái gì đó tương tự.

CẬP NHẬT: Kể từ Bash 4.2, dường như đây là một tính năng được tích hợp sẵn:

echo $'\u0965'

CẬP NHẬT: Ngoài ra, ngày nay, một ví dụ Python có thể sẽ được ưa thích hơn Perl. Điều này hoạt động trong cả Python 2 và 3:

python -c 'print(u"\u0965")'

Cảm ơn ... một perl rất hay và ngắn gọn, nhưng tôi có một chút bối rối về cách nó biết đối xử với giá trị như UTF-16BE .. Tôi đoán đó là "chr" nghĩa là gì ...
Peter.O

@fred đó là một điểm tốt. Ví dụ Perl là địa phương nhạy cảm. -C cho phép xử lý Unicode đầy đủ, nhưng ví dụ này hoạt động vì ngôn ngữ của tôi sử dụng ví dụ Unicode. Nếu tôi đặt LANG thành C, tôi sẽ nhận được cảnh báo về một ký tự rộng trong bản in, nhưng nó vẫn in. Nếu tôi in chr 0xa2bằng ngôn ngữ UTF-8, tôi nhận được ký hiệu ¢, nhưng nếu tôi sử dụng LANG = C, tôi sẽ nhận được vì nó in ra byte 0xa2 không hợp lệ trong UTF-8. Ví dụ Vim / GVim bán nhạy cảm với miền địa phương. Chính xác hơn, để mã hóa tập tin. Nếu bạn bắt đầu Vim ở một địa điểm không phải UTF-8, bạn sẽ cần:set encoding=utf-8
penguin359

@fred Tôi nên chỉ ra, Perl coi giá trị của chr là một điểm mã Unicode nếu Perl được bắt đầu trong một ngôn ngữ Unicode như UTF-8. Một mật mã là số duy nhất đại diện cho một ký tự và không bị ràng buộc với bất kỳ một mã hóa nào, chẳng hạn như UTF-16BE hoặc UTF-8. Nó chuyển đổi nó thành mã hóa chính xác khi in ra. Ví dụ, Dấu hiệu Cuneiform A là tên mã U + 012000. Tôi có thể sử dụng chr 0x12000trong Perl (giả sử Unicode đang hoạt động) để thể hiện nó. Trong UTF-16BE, đây là 0xd8, 0x08, 0xdc và 0x00. Ký tự của bạn là U + 0965, tình cờ là các byte 0x09 theo sau là 0x65 trong UTF-16BE.
chim cánh cụt359

@ penguin359 .. Cảm ơn, một ngày nào đó (hy vọng) tôi sẽ có một cái nhìn tốt về perl .. Nó có vẻ khó hiểu, nhưng sau đó, sed và regex cũng vậy, ban đầu, và bây giờ thì khá dễ dàng ... có lẽ là một chút thích vim; một đường cong học tập dốc, sau đó chèo thuyền đơn giản .... Thật tốt khi đọc lời giải thích của bạn ... nó mở đường ..
Peter.O

Tôi vừa (phát hiện lại) rằng linh hồn printf của Steven D sẽ không xử lý khối ASCII của dải unicode, vì vậy perlcâu trả lời của bạn bây giờ là tốt nhất (đối với các yêu cầu cụ thể của tôi) .. Trước đây tôi đã loại trừ printf (vài tháng trước) , nhưng tôi đã quên nó. Dưới đây là câu hỏi / câu trả lời về các giới hạn của nó ... Tại sao printf báo cáo lỗi trên tất cả ngoại trừ ba Codepoint Unicode (phạm vi ASCII)
Peter.O

13

Bash 4.2 (phát hành năm 2011) thêm hỗ trợ cho echo -e '\u0965', printf '\u0965', printf %b '\u0965'echo $'\u0965'cũng làm việc.

http://tiswww.case.edu/php/chet/bash/FAQ :

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.

Cảm ơn ... Tôi vẫn chủ yếu sử dụng bash 4.1.5 trong Ubuntu 10.04, nhưng chắc chắn rất tốt khi biết rằng nó hiện đã có sẵn trong 4.2. (+1)
Peter.O

1
+1; lưu ý rằng bash 4.2.xcác phiên bản có lỗi trong đó các giá trị giữa 0x800xff( 128 - 255) - tức là trong phạm vi ASCII mở rộng - KHÔNG được mã hóa UTF8 chính xác và thay vào đó chỉ được chuyển qua, dẫn đến char UTF8 không hợp lệ mà một số thiết bị đầu cuối hiển thị như ?. Kể từ (ít nhất) 4.3.11điều này đã được sửa chữa; nếu echo $'\ued'ám í, thì lỗi không xuất hiện.
mkuity0

5

Nếu bạn có lõi GNU, hãy thử printf:

$ printf '\u0965\n'

echo có thể thực hiện công việc nếu bảng điều khiển của bạn đang sử dụng UTF-8 và bạn có mã hóa UTF-8:

$ echo -e '\xE0\xA5\xA5'

Bạn có thể tìm thấy bảng mã hóa hex từ Unicode sang UTF-8 tại đây: http://www.utf8-chartable.de/ . Bạn có thể chuyển đổi các điểm mã Unicode thành hex bằng một số ngôn ngữ script. Dưới đây là một ví dụ sử dụng python:

python -c "print(unichr(int('0965', 16)).encode('utf-8').encode('hex'))"

Sau đây là tập lệnh Perl sẽ chuyển đổi các đối số thành giá trị hex chính xác (nhiều dấu ngoặc đơn không cần thiết ở đây):

#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Encode;

foreach (@ARGV) {
    say unpack('H*', encode('utf8', chr(hex($_))))
}

Ví dụ,

./uni2utf 0965
e0a5a5

Tất nhiên, nếu bạn có Perl hoặc Python, bạn cũng có thể sử dụng chúng để in các ký tự.


Cảm ơn .. echoSẽ không làm những gì tôi muốn, vì Codepoint là UTF-16 Big-Endian 2 byte .. nhưng bạn đã nhắc nhở tôi rằng có 2 hàm printf! (Tôi nghĩ printf có thể làm được, và có vẻ như tôi đã gọi sai) ... $(which printf)hoạt động ... Cảm ơn ví dụ về con trăn .. nhưng vì điều này (đường cong học tập của tôi), tôi đang cố gắng bám sát có thể "bash" như ngôn ngữ kiểm tra duy nhất có liên quan .. (khi tôi đủ thoải mái với bash, tôi sẽ bị mắc kẹt với Python ... btw, .encode('hex')là một bước vượt xa những gì tôi cần .. (Tôi nghĩ rằng nó trông như thế một chút bận rộn ở đó :)
Peter.O

Vâng, .encode ('hex') chỉ để lấy mã hex dường như hoạt động với echo cho tôi. Vui mừng rằng ít nhất một phần của điều này là hữu ích.
Steven D

Tôi vừa mới thấy bạn perl snippet .. cảm ơn ... thật tốt khi có các giải pháp khác nhau được lập bảng ... Bản in là chính xác những gì tôi đang tìm kiếm (một lệnh duy nhất, theo ví dụ zsh) ... .. Tôi có thể đăng tốt phương thức ngôn ngữ không sử dụng kịch bản lệnh khác của tôi , hoạt động trên luồng dữ liệu hex (không \ u, v.v.) ..
Peter.O

Tôi đặc biệt thích sự ngắn gọn của phần printftrên, nhưng nó không xử lý các giá trị bên dưới ... I've just re-discovered something I already knew (but dropped off the radar)... Here is a Question I asked about 4 months ago; [Why does printf report an error on all but three (ASCII-range) Unicode Codepoints](http://askubuntu.com/questions/20806/why-does-printf-report-an-error-on-all-but-three-ascii-range-unicode-codepoints)... So *penguin359's* giải pháp `` \ u00A0 perl` hiện đang trông khá tốt :) .. Đó là một invocaton duy nhất và tôi sau khi "dễ gõ", vì vậy tôi sẽ đưa ra anh ấy đánh dấu màu xanh choperl
Peter.O

2

CẬP NHẬT: Đây là một cách bash để thực hiện một giá trị Unicode duy nhất ... (bằng cách "bash" Ý tôi là: không sử dụng bất kỳ ngôn ngữ kịch bản lệnh nào khác) .. cảm ơn Gilles cho một gợi ý trong Q / A Askubfox này .
Theo liên kết này : recode (Obsoletes iconv, dos2unix, unix2dos) .. Chỉnh sửa: nhưng theo nhận xét bên dưới, "obsoletes 'có thể chỉ có nghĩa là" thay thế "

      echo -n 0x0965 |recode UTF-16BE/x4..UTF-8

Đây là một phương pháp để xử lý kết xuất hex thô làm đầu vào (nghĩa là không có tiền tố thoát như; \ u0965 và không \ x09 \ x65) ..
xxdlà tiện ích kết xuất hex (được đóng gói vim-common) có thể hoàn nguyên kết xuất hex thô đối với các ký tự, kết xuất đại diện cho ... Codepoint Unicode là UTF-16BigEndian, chính xác đó là kết xuất Hex là gì ..
xxdtrong chế độ hoàn nguyên chấp nhận một luồng các giá trị Hex có ngắt dòng.

Kịch bản lệnh này tạo ra một luồng UTF-16BE, sau đó nó sẽ trở lại ký tự gốc.
Dòng cuối cùng chứa hai lệnh cần thiết; xxdiconv

for line in \
  "Matsuo Basho (1644-1694)" \
  "  pond" \
  "  frog jumps in" \
  "  plop!"
do 
  echo "$line" |iconv -f "$(locale charmap)" -t "UTF-16BE" |xxd -ps -u 
done |
#    (---this is the **revert** code---) 
tee >(xxd -p -u -r |iconv -f "UTF-16BE") ;echo

Đây là đầu ra (đầu tiên hiển thị đầu vào hex-dump UTF-16BE).
Chú thích; xxdphân đoạn đầu ra của chính nó với một dòng mới ở 60 chữ số ... Tùy chọn hoàn nguyên bỏ qua các dòng mới này .. nó bỏ qua bất kỳ / tất cả các dòng mới (vì không phải là chữ số hex) ..

004D0061007400730075006F00200042006100730068006F002000280031
003600340034002D00310036003900340029000A
002000200070006F006E0064000A
0020002000660072006F00670020006A0075006D0070007300200069006E
000A
002000200070006C006F00700021000A

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!

Vì có vẻ như bạn đã sử dụng thông tin của chim cánh cụt trong câu trả lời của mình, bạn có thể xem việc đánh dấu câu trả lời của anh ấy là chính xác thay vì của tôi.
Steven D

@Steven D: một nhận xét đáng chú ý, nhưng "dường như" là từ hoạt động. Tôi đã sử dụng iconv như thế này được vài ngày rồi, điều này khiến tôi băn khoăn không biết có một lệnh nào không. Tôi đã thực hiện xử lý toàn bộ tệp tương tự trong windows (C ++), vì vậy tôi có một sự hiểu biết hợp lý về Unicode. Tôi đã thực sự sau một bashphương pháp nhanh chóng và đơn giản . Ý tôi là "bash": sử dụng ngôn ngữ kịch bản bash; không python / perl từ trong bash). Tôi đã thêm điều này như một câu trả lời vì nó có thể có giá trị đối với người đang đọc trang này. Nó là một lót tốt cho toàn bộ tập tin. Bạn printflà câu trả lời tốt nhất cho tôi.
Peter.O

2
Tôi sẽ không nói recode obsoletes iconv, trên thực tế recode cũ hơn iconv và ngày nay iconv được cài đặt mặc định hơn nhiều so với recode (ví dụ, trên Linux, iconv hầu như luôn được cài đặt vì nó đi kèm với libc).
Gilles 'SO- ngừng trở nên xấu xa'

Cảm ơn .. Tôi đã tự hỏi về điều đó .. Trang web đó không chính xác là tài liệu tham khảo dứt khoát ... vì vậy nó là một giải pháp thay thế ...
Peter.O

1

Giả sử mã hóa mặc định cho HĐH của bạn là UTF-8 (đúng với hầu hết các bản phát hành hiện tại) thì bạn có thể sử dụng bash trực tiếp để chuyển đổi bất kỳ điểm mã UNICODE nào:

echo -e "Unicode Character 'DEVANAGARI DOUBLE DANDA' (U+0965) \U0965"

Tất nhiên, glyph sẽ xuất hiện chính xác chỉ khi bạn có phông chữ chính xác. Kể từ bash 4.3, tất cả các điểm mã sẽ hoạt động chính xác. Và hai tùy chọn dựng sẵn này cũng sẽ hoạt động:

printf "%b" "Unicode Character (U+0965) \U0965 \n"
echo $'Unicode Character (U+0965) \U0965'

Lưu ý rằng đối với bash 4.2, mã Unicode 0x80sẽ 0xFFđược mã hóa sai (lỗi bash). Để giải quyết vấn đề này, bạn phải xem chương trình tại trang web này (cũng tốt cho một cái nhìn sâu sắc về vấn đề chuyển đổi số thành ký tự.


Làm việc cho tôi trong bash 4.3 và zsh. Có một báo cáo lỗi cho bash 4.2 mà bạn có thể liên kết đến?
Mikel

Đây có vẻ như là lỗi chính xác: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlMô tả: \ u và \ U mã hóa sai các giá trị giữa \ u80 và \ uff

0

Sử dụng thay thế Mẫu trong bash phiên bản 4.2 (và cao hơn):

${parameter/pattern/string}

như được mô tả ở đây http://steve-parker.org/sh/tips/potype-substlation/

UNICODE_HEX="U+02211"
printf ${UNICODE_HEX/U+/"\U"}


UNICODE_HEX="U+03BB"
printf ${UNICODE_HEX/U+/"\U"}
λ         

1
Lưu ý rằng, như đã nêu trong câu trả lời trước , điều này chỉ hoạt động trong phiên bản bash 4.2 (và cao hơn). Trong thực tế, điều này thêm khá ít vào câu trả lời trước đó.
G-Man nói 'Phục hồi Monica'
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.