Bagaimana cara mencetak karakter ASCII dengan titik kode berbeda di Bash?


12

Dalam tabel ASCII karakter 'J' ada yang memiliki poin kode dalam sistem angka yang berbeda:

Oct   Dec   Hex   Char
112   74    4A    J

Mungkin untuk mencetak arang ini dengan titik kode oktal dengan mencetak printf '\112'atau echo $'\112'. Bagaimana cara mencetak karakter yang sama dengan presentasi titik kode desimal dan heksadesimal?


Jawaban:


12

Hex:

printf '\x4a'

Desember:

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

Alternatif untuk hex :-)

xxd -r <<<'0 4a'

Untungnya ini juga berfungsi di awk.
Sridhar Sarnobat


6

Secara umum, shell dapat memahami hex, oct dan angka desimal dalam variabel, asalkan telah didefinisikan sebagai 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

Atau mereka adalah hasil dari "Ekspansi Aritmatika":

$ : $(( 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

Jadi, Anda hanya perlu satu cara untuk mencetak karakter yang memiliki nilai variabel.
Tapi di sini ada dua cara yang mungkin:

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

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

Dua printf diperlukan, satu untuk mengubah nilai menjadi string heksadesimal dan yang kedua untuk benar-benar mencetak karakter.

Yang kedua akan mencetak titik UNICODE (jika konsol Anda diatur dengan benar).
Sebagai contoh:

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

Manusia salju.

Karakter yang memiliki representasi utf-8 apa f0 9f 90 aeadanya 0x1F42E. Mencari cow face site:fileformat.infountuk mendapatkannya :

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

Catatan : Ada masalah dengan cara UNICODE untuk bash sebelum 4.3 (dikoreksi dalam versi itu dan ke atas), karakter antara UNICODE titik 128 dan 255 (dalam desimal) mungkin dicetak secara tidak benar.


Referensi

Paragraf keempat di PARAMETERSdalam man bash:

Jika variabel memiliki set atribut integernya, maka nilai dievaluasi sebagai ekspresi aritmatika bahkan jika ekspansi $ ((...)) tidak digunakan (lihat Ekspansi Aritmatika di bawah).

Di dalam "EVALUASI ARITHMETIC" di man bash:

Konstanta dengan awalan 0 ditafsirkan sebagai angka oktal. Sebuah 0x atau 0X terkemuka menunjukkan heksadesimal. Kalau tidak, angka-angkanya mengambil bentuk [basis #] n, di mana basis opsional adalah angka desimal antara 2 dan 64 yang mewakili basis aritmatika, dan n adalah angka dalam basis itu. Jika basis # dihilangkan, maka basis 10 digunakan. Digit lebih besar dari 9 diwakili oleh huruf kecil, huruf besar, @, dan _, dalam urutan itu. Jika basis kurang dari atau sama dengan 36, huruf kecil dan huruf besar dapat digunakan secara bergantian untuk mewakili angka antara 10 dan 35.


@ StéphaneChazelas Ya, codepoint bukan (selalu) nilai byte. Bash (dalam versi sebelum 4.3) memberikan nilai byte dari titik kode. Yaitu: karakter é(Oktal: 351, Des: 233, Hex: 0xE9) salah dicetak printf '\351'karena mencetak nilai byte 0xE9selalu. Untuk terminal dengan pengkodean ISO-8859-1(dan sepupu) yang mungkin berfungsi, tetapi di terminal yang dikodekan utf-8, nilai byte dari 0xE9akan muncul sebagai . cont ....
Isaac

@ StéphaneChazelas Saya bukan orang pertama yang memperhatikan dan mencari "bash 4.2 salah encode" untuk satu contoh. Telah diperbaiki dari bash 4.3 dan lebih tinggi.
Isaac

BAIK. Saya mengerti maksud Anda sekarang (saya menguji dengan 4.3 sesuai versi sebelumnya dari jawaban Anda). Perhatikan bahwa ini hanya bash-4.2, bash-4.1 tidak mendukung \u(yang berasal dari zsh).
Stéphane Chazelas


0

Anda dapat menggunakan perpustakaan 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

Jika Anda memiliki daftar nomor untuk dikonversi, dan ingin menghindari panggilan fungsi dan membuat subkulit untuk setiap karakter, Anda dapat menentukan set ascii sebelumnya:

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

Perhatikan bahwa null char dikecualikan, jadi setiap char diimbangi oleh 1.

Kemudian gunakan sesuatu seperti ini (mengasumsikan 1 angka per baris):

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

0

Inilah semua konversi yang digunakan 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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.