Làm cách nào để in một ký tự ASCII theo các điểm mã khác nhau trong Bash?


12

Trong bảng ASCII, ký tự 'J' tồn tại có các điểm mã trong các hệ thống số khác nhau:

Oct   Dec   Hex   Char
112   74    4A    J

Có thể in char này bằng một điểm mã bát phân bằng cách in printf '\112'hoặc echo $'\112'. Làm cách nào để in cùng một ký tự bằng cách trình bày điểm mã thập phân và thập lục phân?


Câu trả lời:


12

Lục giác:

printf '\x4a'

Tháng 12:

printf "\\$(printf %o 74)"

Thay thế cho hex :-)

xxd -r <<<'0 4a'

Rất may điều này cũng hoạt động trong awk.
Sridhar Sarnobat


6

Nói chung, shell có thể hiểu các số hex, oct và thập phân trong các biến, miễn là chúng được định nghĩa là integers:

$ declare -i v1 v2 v3 v4 v5 v6 v7
$ v1=0112
$ v2=74
$ v3=0x4a
$ v4=8#112
$ v5=10#74
$ v6=16#4a
$ v7=18#gg
echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304

Hoặc chúng là kết quả của "Mở rộng số học":

$ : $(( v1=0112, v2=74, v3=0x4a, v4=8#112, v5=10#74, v6=16#4a, v7=18#gg ))
$ echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304

Vì vậy, bạn chỉ cần một cách để in ký tự thuộc về một giá trị biến.
Nhưng đây là hai cách có thể:

$ var=$((0x65))
$ printf '%b\n' "\\$(printf '0%o' "$var")"
e

$ declare -i var
$ var=0x65; printf '%b\n' "\U$(printf '%08x' "$var")"
e

Cần có hai printf, một để chuyển đổi giá trị thành chuỗi thập lục phân và chuỗi thứ hai để thực sự in ký tự.

Cái thứ hai sẽ in bất kỳ điểm UNICODE nào (nếu bảng điều khiển của bạn được đặt chính xác).
Ví dụ:

$ var=0x2603; printf '%b\n' "\U$(printf '%08x' "$var")"

Một người tuyết.

Các nhân vật có đại diện utf-8 f0 9f 90 ae0x1F42E. Tìm kiếm cow face site:fileformat.infođể có được nó :

$ var=0x1F42F; printf '%b\n' "\U$(printf '%08x' "$var")"
🐮

Lưu ý : Có một vấn đề với cách UNICODE trong đó đối với bash trước 4.3 (được sửa trong phiên bản đó trở lên), các ký tự giữa UNICODE điểm 128 và 255 (ở dạng thập phân) có thể được in không chính xác.


Người giới thiệu

Đoạn thứ tư bên PARAMETERStrong man bash:

Nếu biến có tập thuộc tính số nguyên, thì giá trị được đánh giá là biểu thức số học ngay cả khi mở rộng $ ((...)) không được sử dụng (xem Mở rộng số học bên dưới).

Bên trong "ĐÁNH GIÁ ARITHMETIC" trong man bash:

Các hằng số có 0 đứng đầu được hiểu là các số bát phân. Số 0x hoặc 0X hàng đầu biểu thị thập lục phân. Mặt khác, các số có dạng [cơ sở #] n, trong đó cơ sở tùy chọn là số thập phân từ 2 đến 64 đại diện cho cơ sở số học và n là một số trong cơ sở đó. Nếu cơ sở # bị bỏ qua, thì cơ sở 10 được sử dụng. Các chữ số lớn hơn 9 được biểu thị bằng các chữ cái viết thường, các chữ cái viết hoa, @ và _, theo thứ tự đó. Nếu cơ sở nhỏ hơn hoặc bằng 36, chữ cái viết thường và chữ hoa có thể được sử dụng thay thế cho nhau để thể hiện các số từ 10 đến 35.


@ StéphaneChazelas Chà, một mật mã không (luôn luôn) là một giá trị byte. Bash (trong các phiên bản trước 4.3) cung cấp giá trị byte của điểm mã. Đó là: ký tự é(Octal: 351, Dec: 233, Hex: 0xE9) được in không chính xác printf '\351'vì nó in một giá trị byte 0xE9luôn. Đối với một thiết bị đầu cuối có mã hóa ISO-8859-1(và anh em họ) có thể hoạt động, nhưng trong các thiết bị đầu cuối được mã hóa utf-8, giá trị byte 0xE9sẽ xuất hiện dưới dạng. tiếp ....
Isaac

@ StéphaneChazelas Tôi không phải là người đầu tiên nhận thấy và tìm kiếm "bash 4.2 mã hóa không chính xác" cho một ví dụ. Nó đã được sửa từ bash 4.3 trở lên.
Isaac

ĐỒNG Ý. Tôi hiểu ý của bạn lúc này (Tôi đã thử nghiệm với 4.3 theo phiên bản trước của câu trả lời của bạn). Lưu ý rằng đó chỉ là bash-4.2, bash-4.1 không hỗ trợ \u(xuất phát từ zsh).
Stéphane Chazelas


0

Bạn có thể sử dụng thư viện stdlib POSIX Awk :

$ awklib 'BEGIN {print str_chr(74)}'
J

$ awklib 'BEGIN {print str_chr(+base_conv("4A", 16, 10))}'
J

$ awklib 'BEGIN {print str_chr(+base_conv(112, 8, 10))}'
J

$ awklib 'BEGIN {print str_chr(+base_conv(1001010, 2, 10))}'
J

0

Nếu bạn có một danh sách các số cần chuyển đổi và muốn tránh một lệnh gọi hàm và tạo một lớp con cho mỗi ký tự, bạn có thể xác định bộ ascii được đặt trước:

ascii=$(for x in {0..9} {A..F}; do for y in {0..9} {A..F}; do echo -ne "\x$x$y"; done; done)

Lưu ý rằng null char được loại trừ, vì vậy mọi char được bù 1.

Sau đó sử dụng một cái gì đó như thế này (giả sử 1 số trên mỗi dòng):

while read c; do out+="${ascii:$c-1:1}"; done <<< "$in"
echo "$out"

0

Đây là tất cả các chuyển đổi bằng cách sử dụng printf:

printf "%o" "'J" # 112 (oct)
printf "%d" "'J" # 74 (dec)
printf "%x" "'J" # 4a (hex)

printf '\112' # J (oct)
printf "\x$(printf %x 74)" # J (dec, requires double conversion)
printf '\x4a' # J (hex)
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.