Come faccio a stampare un carattere ASCII con punti di codice diversi in Bash?


12

Nella tabella ASCII esiste il carattere 'J' che ha punti di codice in diversi sistemi numerici:

Oct   Dec   Hex   Char
112   74    4A    J

È possibile stampare questo carattere con un punto di codice ottale stampando printf '\112'o echo $'\112'. Come stampare lo stesso carattere con presentazioni in punti decimali ed esadecimali?


Risposte:


12

Esadecimale:

printf '\x4a'

Dec:

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

Alternativa per hex :-)

xxd -r <<<'0 4a'

Per fortuna anche questo funziona awk.
Sridhar Sarnobat,


6

In generale, la shell potrebbe comprendere numeri esadecimali, ottici e decimali in variabili, a condizione che siano stati definiti come 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

Oppure sono il risultato di una "espansione aritmetica":

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

Quindi, hai solo bisogno di un modo per stampare il carattere che appartiene a un valore variabile.
Ma qui ci sono due modi possibili:

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

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

Sono necessari i due printf, uno per trasformare il valore in una stringa esadecimale e il secondo per stampare effettivamente il carattere.

Il secondo stamperà qualsiasi punto UNICODE (se la tua console è impostata correttamente).
Per esempio:

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

Un uomo di neve.

Il personaggio che ha una rappresentazione utf-8 come f0 9f 90 aeè 0x1F42E. Cerca cow face site:fileformat.infoper ottenerlo :

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

Nota : c'è un problema con il modo UNICODE in quanto per bash prima della 4.3 (corretto in quella versione e verso l'alto), i caratteri tra i punti UNICODE 128 e 255 (in decimale) potrebbero essere stampati in modo errato.


Riferimenti

Quarto paragrafo all'interno PARAMETERSdi man bash:

Se la variabile ha l'attributo intero impostato, il valore viene valutato come espressione aritmetica anche se l'espansione $ ((...)) non viene utilizzata (vedere Espansione aritmetica di seguito).

All'interno di "VALUTAZIONE ARITMETICA" in man bash:

Le costanti con uno 0 iniziale sono interpretate come numeri ottali. Uno 0x o 0X iniziale indica esadecimale. Altrimenti, i numeri assumono la forma [base #] n, dove la base opzionale è un numero decimale compreso tra 2 e 64 che rappresenta la base aritmetica e n è un numero in quella base. Se la base # viene omessa, viene utilizzata la base 10. Le cifre superiori a 9 sono rappresentate dalle lettere minuscole, dalle lettere maiuscole, @ e _, in quell'ordine. Se la base è inferiore o uguale a 36, ​​le lettere minuscole e maiuscole possono essere utilizzate in modo intercambiabile per rappresentare numeri compresi tra 10 e 35.


@ StéphaneChazelas Bene, un punto di codice non è (sempre) un valore in byte. Bash (nelle versioni precedenti alla 4.3) fornisce il valore in byte del punto di codice. Cioè: il carattere é(ottale: 351, dic: 233, esadecimale: 0xE9) viene stampato in modo errato printf '\351'poiché stampa un valore byte di 0xE9sempre. Per un terminale con una codifica di ISO-8859-1(e cugini) che può funzionare, ma in terminali codificati utf-8, il valore di byte di 0xE9dovrebbe apparire come . cont ....
Isaac,

@ StéphaneChazelas Non sono il primo a notare e cercare "bash 4.2 codifica erroneamente" per un esempio. È stato corretto da bash 4.3 in poi.
Isacco,

OK. Capisco cosa intendi ora (stavo testando con 4.3 come nella versione precedente della tua risposta). Nota che è solo bash-4.2, bash-4.1 non supportava \u(che proviene da zsh).
Stéphane Chazelas,

5

Decimale:

chr() {
    local c
    for c
    do
        printf "\\$((c/64*100+c%64/8*10+c%8))"
    done
}

chr 74

Esadecimale:

chr $((16#4a))

La funzione può eseguire sequenze:

$ chr 74 75 76; echo
JKL
$

0

È possibile utilizzare la libreria stdlib di 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

Se si dispone di un elenco di numeri da convertire e si desidera evitare una chiamata di funzione e la creazione di una subshell per ciascun carattere, è possibile definire in anticipo il set ASCII:

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

Si noti che il carattere null è escluso, quindi ogni carattere è scostato di 1.

Quindi utilizzare qualcosa del genere (presuppone 1 numero per riga):

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

0

Ecco tutte le conversioni utilizzando 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.