Wie drucke ich ein ASCII-Zeichen mit verschiedenen Codepunkten in Bash?


12

In der ASCII-Tabelle ist das Zeichen 'J' vorhanden, das Codepunkte in verschiedenen Zahlensystemen enthält:

Oct   Dec   Hex   Char
112   74    4A    J

Es ist möglich , diese Zeichen durch einen Oktalcode Punkt zu drucken , durch Druck printf '\112'oder echo $'\112'. Wie drucke ich dasselbe Zeichen durch dezimale und hexadezimale Codepunktpräsentationen?


Antworten:


12

Verhexen:

printf '\x4a'

Dez:

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

Alternative für Hex :-)

xxd -r <<<'0 4a'

Zum Glück funktioniert das auch in awk.
Sridhar Sarnobat


6

Im Allgemeinen kann die Shell Hex-, Okt- und Dezimalzahlen in Variablen verstehen, vorausgesetzt, sie wurden wie folgt definiert 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

Oder sie sind das Ergebnis einer "arithmetischen Erweiterung":

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

Sie brauchen also nur eine Möglichkeit, um das Zeichen zu drucken, das zu einem variablen Wert gehört.
Aber hier sind zwei Möglichkeiten:

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

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

Die beiden printf werden benötigt, eine, um den Wert in eine hexadezimale Zeichenfolge umzuwandeln, und die zweite, um das Zeichen tatsächlich zu drucken.

Der zweite druckt einen beliebigen UNICODE-Punkt (wenn Ihre Konsole richtig eingestellt ist).
Beispielsweise:

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

Ein Schneemann.

Das Zeichen, das eine utf-8-Darstellung f0 9f 90 aehat 0x1F42E. Suchen Sie nach cow face site:fileformat.info, um es zu bekommen :

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

Hinweis : Es gibt ein Problem mit der UNICODE-Methode, da für Bash vor 4.3 (korrigiert in dieser Version und höher) die Zeichen zwischen den UNICODE-Punkten 128 und 255 (in Dezimalzahlen) möglicherweise falsch gedruckt werden.


Verweise

Vierter Absatz innen PARAMETERSin man bash:

Wenn für die Variable das Integer-Attribut festgelegt ist, wird der Wert als arithmetischer Ausdruck ausgewertet, auch wenn die Erweiterung $ ((...)) nicht verwendet wird (siehe Arithmetische Erweiterung unten).

Innerhalb von "ARITHMETIC EVALUATION" in man bash:

Konstanten mit einer führenden 0 werden als Oktalzahlen interpretiert. Ein führendes 0x oder 0X bedeutet hexadezimal. Andernfalls haben Zahlen die Form [Basis #] n, wobei die optionale Basis eine Dezimalzahl zwischen 2 und 64 ist, die die arithmetische Basis darstellt, und n eine Zahl in dieser Basis ist. Wenn die Basis # weggelassen wird, wird die Basis 10 verwendet. Die Ziffern größer als 9 werden durch die Kleinbuchstaben, die Großbuchstaben @ und _ in dieser Reihenfolge dargestellt. Wenn die Basis kleiner oder gleich 36 ist, können Klein- und Großbuchstaben austauschbar verwendet werden, um Zahlen zwischen 10 und 35 darzustellen.


@ StéphaneChazelas Nun, ein Codepunkt ist (immer) kein Bytewert. Bash (in Versionen vor 4.3) liefert den Bytewert des Codepunkts. Das heißt: Das Zeichen é(Oktal: 351, Dez: 233, Hex: 0xE9) wird falsch gedruckt, printf '\351'da es einen Bytewert von 0xE9immer druckt . Für ein Terminal mit einer Codierung von ISO-8859-1(und Cousins), die möglicherweise funktioniert, aber in utf-8-codierten Terminals sollte ein Bytewert von 0xE9als angezeigt werden. Fortsetzung ....
Sorontar

@ StéphaneChazelas Ich bin nicht der erste, der ein Beispiel bemerkt und nach "bash 4.2 falsch codiert" sucht. Es wurde ab Bash 4.3 und höher korrigiert.
Sorontar

IN ORDNUNG. Ich verstehe, was Sie jetzt meinen (ich habe mit 4.3 gemäß der früheren Version Ihrer Antwort getestet). Beachten Sie, dass es nur bash-4.2 ist, bash-4.1 nicht unterstützt \u(was von zsh kommt).
Stéphane Chazelas

5

Dezimal:

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

chr 74

Verhexen:

chr $((16#4a))

Die Funktion kann Sequenzen ausführen:

$ chr 74 75 76; echo
JKL
$

0

Sie können die POSIX Awk stdlib-Bibliothek verwenden :

$ 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

Wenn Sie eine Liste mit zu konvertierenden Zahlen haben und einen Funktionsaufruf vermeiden und für jedes Zeichen eine Unterschale erstellen möchten, können Sie den ASCII-Satz im Voraus definieren:

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

Beachten Sie, dass Nullzeichen ausgeschlossen sind, sodass jedes Zeichen um 1 versetzt ist.

Verwenden Sie dann so etwas (setzt 1 Nummer pro Zeile voraus):

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

0

Hier sind alle Conversions mit 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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.