Как мне распечатать символ ASCII с помощью разных кодовых точек в Bash?


12

В таблице ASCII существует символ «J», который имеет кодовые точки в разных системах счисления:

Oct   Dec   Hex   Char
112   74    4A    J

Этот символ можно распечатать восьмеричным кодом, напечатав printf '\112'или echo $'\112'. Как мне распечатать один и тот же символ в десятичной и шестнадцатеричной кодовой форме?


Ответы:



6

С zsh:

$ printf '\x4a\n' # Hex
J
$ printf "\\$(([##8]74))\n" # Dec
J

Чтобы получить символ (в текущей кодировке) из кодовой точки Unicode:

$ printf '\U1F42E\n' # Hex
🐮
$ printf "\\U$(([##16]128046))\n" # Dec
🐮

Ответьте также, пожалуйста, как напечатать это лицо с помощью шестнадцатеричного кода 'f0 9f 90 ae'
viavad


6

В общем, оболочка может понимать шестнадцатеричные, октальные и десятичные числа в переменных при условии, что они определены как 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

Или они являются результатом «арифметического расширения»:

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

Итак, вам просто нужен один способ напечатать символ, который принадлежит значению переменной.
Но вот два возможных пути:

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

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

Необходимы два printf: один для преобразования значения в шестнадцатеричную строку, а второй для фактической печати символа.

Второй выводит любую точку UNICODE (если ваша консоль установлена ​​правильно).
Например:

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

Снежный человек.

Символ, который имеет представление utf-8 как f0 9f 90 aeесть 0x1F42E. Ищите, cow face site:fileformat.infoчтобы получить это :

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

Примечание . Проблема, связанная со способом UNICODE, заключается в том, что для bash до версии 4.3 (исправлено в этой версии и выше) символы между точками UNICODE 128 и 255 (в десятичном формате) могут быть напечатаны неправильно.


Ссылки

Четвертый абзац внутри PARAMETERSв man bash:

Если переменная имеет свой целочисленный атрибут, то значение оценивается как арифметическое выражение, даже если расширение $ ((...)) не используется (см. Арифметическое расширение ниже).

Внутри "АРИФМЕТИЧЕСКАЯ ОЦЕНКА" в man bash:

Константы с ведущим 0 интерпретируются как восьмеричные числа. Ведущий 0x или 0X обозначает шестнадцатеричное. В противном случае числа принимают форму [base #] n, где необязательное основание - это десятичное число от 2 до 64, представляющее арифметическое основание, а n - это число в этом основании. Если база # опущена, то используется база 10. Цифры больше 9 представлены строчными буквами, заглавными буквами @ и _ в указанном порядке. Если основание меньше или равно 36, строчные и прописные буквы могут использоваться взаимозаменяемо для представления чисел от 10 до 35.


@ StéphaneChazelas Ну, кодовая точка не всегда является байтовым значением. Bash (в версиях до 4.3) предоставляет байтовое значение кодовой точки. То есть: символ é(Octal: 351, Dec: 233, Hex: 0xE9) напечатан неправильно, printf '\351'поскольку он печатает значение байта 0xE9всегда. Для терминала с кодировкой ISO-8859-1(и двоюродных братьев), который может работать, но в терминалах с кодировкой utf-8, значение байта 0xE9должно отображаться как . продолжение ....
Исаак

@ StéphaneChazelas Я не первый, кто замечает и ищет «неправильно кодирует bash 4.2» для одного примера. Это было исправлено с bash 4.3 и выше.
Исаак

ОК. Теперь я понимаю, что вы имеете в виду (я тестировал с 4.3 согласно предыдущей версии вашего ответа). Обратите внимание, что это только bash-4.2, bash-4.1 не поддерживает \u(что происходит от zsh).
Стефан Шазелас



0

Если у вас есть список чисел для преобразования, и вы хотите избежать вызова функции и создания подоболочки для каждого символа, вы можете заранее определить набор 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)

Обратите внимание, что нулевой символ исключен, поэтому каждый символ смещается на 1.

Затем используйте что-то вроде этого (предполагается, что 1 номер в строке):

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

0

Вот все преобразования, использующие 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.