Jak wydrukować znak ASCII według różnych punktów kodowych w Bash?


12

W tabeli ASCII istnieje znak „J”, który ma punkty kodowe w różnych systemach liczbowych:

Oct   Dec   Hex   Char
112   74    4A    J

Możliwe jest wydrukowanie tego znaku za pomocą ósemkowego punktu kodowego poprzez wydrukowanie printf '\112'lub echo $'\112'. Jak wydrukować ten sam znak za pomocą dziesiętnych i szesnastkowych prezentacji punktów kodowych?


Odpowiedzi:


12

Klątwa:

printf '\x4a'

Grudzień:

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

Alternatywa dla hexa :-)

xxd -r <<<'0 4a'

Na szczęście to też działa awk.
Sridhar Sarnobat


6

Ogólnie rzecz biorąc, powłoka może zrozumieć liczby szesnastkowe, ósemkowe i dziesiętne w zmiennych, pod warunkiem że zostały one zdefiniowane jako 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

Lub są wynikiem „rozszerzenia arytmetycznego”:

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

Potrzebujesz tylko jednego sposobu, aby wydrukować znak należący do wartości zmiennej.
Ale są dwa możliwe sposoby:

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

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

Potrzebne są dwa printf, jeden do przekształcenia wartości w łańcuch szesnastkowy, a drugi do wydrukowania znaku.

Drugi wydrukuje dowolny punkt UNICODE (jeśli konsola jest poprawnie ustawiona).
Na przykład:

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

Śnieżny człowiek.

Postać, która ma reprezentację utf-8 taką, jaka f0 9f 90 aejest 0x1F42E. Wyszukaj, cow face site:fileformat.infoaby uzyskać :

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

Uwaga : Występuje problem ze sposobem UNICODE polegający na tym, że w przypadku bash przed 4.3 (poprawionym w tej wersji i wyżej), znaki między punktami UNICODE 128 i 255 (dziesiętnie) mogą być niepoprawnie wydrukowane.


Bibliografia

Czwarty akapit PARAMETERSw man bash:

Jeśli zmienna ma ustawiony atrybut liczby całkowitej, wówczas wartość jest obliczana jako wyrażenie arytmetyczne, nawet jeśli nie jest używane rozwinięcie $ ((...)) (patrz rozwinięcie arytmetyczne poniżej).

Wewnątrz „OCENY ARYTETYCZNEJ” w man bash:

Stałe z wiodącym 0 są interpretowane jako liczby ósemkowe. Początkowe 0x lub 0X oznacza liczbę szesnastkową. W przeciwnym razie liczby przyjmują postać [podstawa #] n, gdzie opcjonalna podstawa jest liczbą dziesiętną między 2 a 64 reprezentującą podstawę arytmetyczną, a n jest liczbą w tej podstawie. Jeśli baza nr zostanie pominięta, zostanie użyta baza 10. Cyfry większe niż 9 są reprezentowane przez małe litery, wielkie litery @ i _ w tej kolejności. Jeśli podstawa jest mniejsza lub równa 36, ​​małe i wielkie litery mogą być używane zamiennie do reprezentowania liczb od 10 do 35.


@ StéphaneChazelas Cóż, kod nie jest (zawsze) wartością bajtową. Bash (w wersjach wcześniejszych niż 4.3) zapewnia wartość bajtową punktu kodowego. To znaczy: znak é(liczba ósemkowa: 351, grudzień: 233, heksa: 0xE9) jest niepoprawnie wydrukowany, printf '\351'ponieważ wypisuje wartość bajtu 0xE9zawsze. W przypadku terminala z kodowaniem ISO-8859-1(i kuzynów), który może działać, ale w terminalach zakodowanych w utf-8 wartość bajtu 0xE9powinna wynosić . cd ....
Isaac

@ StéphaneChazelas Nie jestem pierwszym, który zauważył i szukał „bash 4.2 niepoprawnie koduje” dla jednego przykładu. Zostało to poprawione z wersji bash 4.3 i wyższej.
Izaak,

OK. Rozumiem, co masz na myśli (testowałem z 4.3 zgodnie z wcześniejszą wersją twojej odpowiedzi). Zauważ, że to tylko bash-4.2, bash-4.1 nie obsługiwał \u(który pochodzi z zsh).
Stéphane Chazelas,

5

Dziesiętny:

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

chr 74

Klątwa:

chr $((16#4a))

Funkcja może wykonywać sekwencje:

$ chr 74 75 76; echo
JKL
$

0

Możesz użyć biblioteki stdlib Awk POSIX :

$ 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

Jeśli masz listę liczb do konwersji i chcesz uniknąć wywołania funkcji i utworzenia podpowłoki dla każdego znaku, możesz wcześniej zdefiniować zestaw 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)

Zauważ, że znak zerowy jest wykluczony, więc każdy znak jest przesunięty o 1.

Następnie użyj czegoś takiego (zakłada 1 liczbę na linię):

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

0

Oto wszystkie konwersje za pomocą 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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.