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.