Як надрукувати символ 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 (виправлено у цій версії та вгору) символи між точками 128 та 255 UNICODE (у десятковій формі) можуть бути неправильно надруковані.


Список літератури

Четвертий пункт всередині 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

Ви можете використовувати бібліотеку stdlib 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

Якщо у вас є список номерів, які потрібно перетворити, і ви хочете уникнути виклику функції та створення підшаровок для кожного символу, ви можете заздалегідь визначити набір 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.