Сценарій Bash, щоб отримати значення ASCII для алфавіту


Відповіді:


70

Визначте ці дві функції (як правило, доступні іншими мовами):

chr() {
  [ "$1" -lt 256 ] || return 1
  printf "\\$(printf '%03o' "$1")"
}

ord() {
  LC_CTYPE=C printf '%d' "'$1"
}

Використання:

chr 65
A

ord A
65

7
@ dmsk80: +1. Для інших , як я , які думають , що вони виявили друкарську помилку: "'A"правильно тоді , якщо ви використовуєте "A"це буде сказати: A: invalid number. Здається, це зроблено на стороні printf (тобто в оболонці, "'A"дійсно , є два знаки, a 'і a), Aякі передаються до printf. як десятковий знак '%d'. Використовуйте, 'Ox%x'щоб показати його в шестикутнику або '0%o'мати його у вісімці))
Олів'є Дулак,

3
-1 для не пояснюючи , як це працює ... жартує: D, але серйозно , що робить це printf "\\$(printf '%03o' "$1")", '%03o', LC_CTYPE=Cі апостроф в "'$1"справах?
razzak

1
Прочитайте всі деталі у FAQ 71 . Відмінний детальний аналіз.

19

Ви можете побачити весь набір за допомогою:

$ man ascii

Ви отримаєте таблиці в восьмеричній, шістнадцятковій і десятковій.


Існує також пакет ascii для дистрибутив на основі debian, але (принаймні зараз) питання позначене як bash, тому це не допоможе ОП. Насправді він встановлений у моїй системі, і все, що я отримую від man ascii, - це його man page.
Джо

12

Якщо ви хочете розширити його на символи UTF-8:

$ perl -CA -le 'print ord shift' 😈
128520

$ perl -CS -le 'print chr shift' 128520
😈

З bash, kshабо zshвбудовані:

$ printf "\U$(printf %08x 128520)\n"
😈

Ви мали намір поставити символ квадратної коробки чи ще, тоді оригінальний знак не відображається у пості і замінюється символом квадратного поля.
mtk

1
@mtk, Вам потрібен браузер, який відображає UTF-8 та шрифт, що має цей 128520 символ.
Стефан Шазелас

Я перебуваю на останньому Chrome, і не думаю, що він не підтримує UTF-8. Хочете знати, на якому браузері ви працюєте?
mtk

@mtk, iceweaselна Debian sid. Шрифт, підтверджений веб-консоллю iceweasel, є "DejaVu Sans", і у мене встановлені додаткові пакети ttf-dejavu ttf-dejavu-core ttf-dejavu-додаткові пакети, які надходять з Debian вгору за течією на dejavu-fonts.org
Stéphane Chazelas

що є базою 128520? мій власний , ctbl()здається, правильно дати мені можливість показати його і нарізати напівкокс з голови колони з printf, але вона ставить 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))в $OPTARGдля байтових значень.
mikeserv

12

Це добре працює,

echo "A" | tr -d "\n" | od -An -t uC

echo "A"                              ### Emit a character.
         | tr -d "\n"                 ### Remove the "newline" character.
                      | od -An -t uC  ### Use od (octal dump) to print:
                                      ### -An  means Address none
                                      ### -t  select a type
                                      ###  u  type is unsigned decimal.
                                      ###  C  of size (one) char.

точно рівнозначно:

echo -n "A" | od -An -tuC        ### Not all shells honor the '-n'.

3
Чи можете ви додати невелике пояснення?
Бернхард

tr, щоб видалити "\ n" (новий рядок) із входу. od використовується для -t dC - це друк у вигляді десяткового символу.
Сараванан

1
echo -nпригнічує tr -d "\n"
зворотний

2
@Gowtham, лише з деякими реалізаціями, наприклад echo, не в сумісних відгомонах Unix. printf %s Aбув би переносним.
Стефан Шазелас

6

Я збираюся просте (і елегантне?) Рішення Bash:

for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done

Для сценарію ви можете використовувати наступне:

CharValue="A"
AscValue=`printf "%d" "'$CharValue"

Зверніть увагу на єдину цитату перед CharValue. Це зобов'язане ...


1
Чим ваша відповідь відрізняється від відповіді dsmsk80?
Бернхард

1
Моє тлумачення питання полягає в тому, як "отримати значення ASCII для значень алфавіту". Не як визначити функцію для отримання значення ASCII для одного символу. Тому моя перша відповідь - це коротка однорядкова команда для отримання значень ASCII для алфавіту.
phulstaert

Я розумію, але я все ще думаю, що підсумком обох відповідей є printf "%d".
Бернхард

2
Я погоджуюся, що це важлива частина процесу, щоб досягти результату, але я не хотів робити припущення, що xmpirate знає про "для i in" та використання діапазону. Якби він хотів список, це може бути економія реального часу ;-). Також майбутні читачі можуть вважати мої доповнення корисними.
phulstaert

6
ctbl()  for O                   in      0 1 2 3
        do  for o               in      0 1 2 3 4 5 6 7
                do for  _o      in      7 6 5 4 3 2 1 0
                        do      case    $((_o=(_o+=O*100+o*10)?_o:200)) in
                                (*00|*77) set   "${1:+ \"}\\$_o${1:-\"}";;
                                (140|42)  set   '\\'"\\$_o$1"           ;;
                                (*)       set   "\\$_o$1"               ;esac
                        done;   printf   "$1";   shift
                done
        done
eval '
ctbl(){
        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"
        for     c in    ${a+"a=$a"} ${b+"b=$b"} ${c+"c=$c"}\
                        ${LC_ALL+"LC_ALL=$LC_ALL"}
        do      while   case  $c in     (*\'\''*) ;; (*) ! \
                                 set "" "${c%%=*}='\''${c#*=}$1'\'' $2" "$3"
                        esac;do  set    "'"'\''\${c##*\'}"'$@";  c=${c%\'\''*}
        done;   done;   LC_ALL=C a=$3 c=;set "" "$2 OPTARG='\''${#a}*("
        while   [ 0 -ne "${#a}" ]
        do      case $a in      ([[:print:][:cntrl:]]*)
                        case    $a in   (['"$(printf \\1-\\77)"']*)
                                        b=0;;   (*)     b=1
                        esac;;  (['"$(  printf  \\200-\\277)"']*)
                                        b=2;;   (*)     b=3
                esac;    set    '"$(ctbl)"'     "$@"
                eval "   set    \"\${$((b+1))%"'\''"${a%"${a#?}"}"*}" "$6"'\''
                a=${a#?};set    "$((b=b*100+${#1}+${#1}/8*2)))" \
                                "$2(o$((c+=1))=$b)>=(d$c=$((0$b)))|"
        done;   eval "   unset   LC_ALL  a b c;${2%?})'\''"
        return  "$((${OPTARG%%\**}-1))"
}'

Перший ctbl()- на вершині там - пробігає лише один раз. Він генерує такий вихід (який був відфільтрований sed -n lзаради друку) :

ctbl | sed -n l

 "\200\001\002\003\004\005\006\a\b\t$
\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\
\035\036\037 !\\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRS\
TUVWXYZ[\\]^_\\`abcdefghijklmnopqrstuvwxyz{|}~\177" "\200\201\202\203\
\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\
\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\
\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\
\267\270\271\272\273\274\275\276\277" "\300\301\302\303\304\305\306\
\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\
\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\
\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\
\372\373\374\375\376\377"$

... які є 8-бітовими байтами (менше NUL) , розділеними на чотири рядки, цитовані оболонкою, розділені рівномірно на 64-байтові межі. Можуть бути представлені рядки з вісімковій діапазони подобається \200\1-\77, \100-\177, \200-\277, \300-\377, де байти 128 використовуються в якості місця для держателя NUL.

Перша ctbl()вся мета існування - генерувати ці рядки, щоб вони evalмогли визначати другу ctbl()функцію з ними буквально вбудованою після цього. Таким чином, на них можна посилатись у функції, без необхідності генерувати їх знову щоразу, коли вони знадобляться. Коли evalвизначається друга ctbl()функція, перша перестане бути.

Верхня половина другої ctbl()функції в основному є допоміжною - вона розроблена для портативної та безпечної серіалізації будь-якого поточного стану оболонки, на яку вона може вплинути, коли вона викликається. Верхній цикл цитуватиме будь-які лапки у значеннях будь-яких змінних, які він може використовувати, а потім укладає всі результати у свої позиційні параметри.

Перші два рядки, однак, спочатку повертають 0 і встановлюють таке $OPTARGж, якщо перший аргумент функції не містить принаймні одного символу. І якщо це так, другий рядок негайно обрізає свій перший аргумент лише його першим символом - тому що функція обробляє символ лише за один раз. Важливо, що це робиться в поточному контексті локалі, а це означає, що якщо символ може містити більше одного байта, тоді, якщо оболонка належним чином підтримує багатобайтові символи, вона не відкине жодних байтів, окрім тих, яких немає в Перший персонаж свого першого аргументу.

        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"

Потім це цикл збереження, якщо він є необхідним, а потім переосмислює поточний контекст локалі для мови C для кожної категорії шляхом присвоєння LC_ALLзмінної. З цього моменту символ може складатися лише з одного байту, і тому, якщо в першому символі його першого аргументу було кілька байтів, тепер вони повинні бути адресовані як окремі символи самостійно.

        LC_ALL=C

Саме з цієї причини друга половина функції є while циклом , на відміну від послідовно запущеної послідовності. У більшості випадків він, ймовірно, виконуватиметься лише один раз за виклик, але, якщо оболонка, у якій ctbl()визначено належним чином, обробляє багатобайтові символи, вона може зациклюватися.

        while   [ 0 -ne "${#a}" ]
        do      case $a in      ([[:print:][:cntrl:]]*)
                        case    $a in   (['"$(printf \\1-\\77)"']*)
                                        b=0;;   (*)     b=1
                        esac;;  (['"$(  printf  \\200-\\277)"']*)
                                        b=2;;   (*)     b=3
                esac;    set    '"$(ctbl)"'     "$@"

Зауважимо, що вищезазначена $(ctbl)заміна команд оцінюється лише один раз - evalколи функція спочатку визначена - і назавжди після цього маркер замінюється буквальним результатом цієї заміни команди, як збережений у пам'яті оболонки. Те саме стосується двох caseпідстановок командної схеми. Ця функція ніколи не викликає підзаголовок або будь-яку іншу команду. Він також ніколи не намагатиметься прочитати або записати вхід / вихід (за винятком випадків діагностичного повідомлення оболонки - що, ймовірно, вказує на помилку) .

Також зауважте, що тест на безперервність циклу проводиться не просто [ -n "$a" ], тому що, як я виявив своє розчарування, чомусь bashоболонка робить:

char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!

but it's not null!

... і тому я чітко порівнюю $a'sn з 0 для кожної ітерації, яка, також незрозуміло, поводиться по-різному (читайте: правильно) .

У caseперевіряє перші байти для включення в будь-якому з наших чотирьох рядків і зберігає посилання на набір байті в $b. Після цього перші чотири позиційні параметри оболонки мають setрядки, вбудовані evalта написані ctbl()попередником.

Далі все, що залишилося від першого аргументу, знову тимчасово прирізається до його першого символу - який тепер повинен бути впевнений, що це єдиний байт. Цей перший байт використовується в якості посилання на смуги з хвоста рядка , які воно відповідає і посилання в $bце eval«d , щоб представляти позиційний параметр таким чином , все від опорного байта до останнього байта в рядку може бути замінений геть. Інші три рядки повністю випадають з позиційних параметрів.

               eval "   set    \"\${$((b+1))%"'\''"${a%"${a#?}"}"*}" "$6"'\''
               a=${a#?};set    "$((b=b*100+${#1}+${#1}/8*2)))" \
                                "$2(o$((c+=1))=$b)>=(d$c=$((0$b)))|"

У цей момент значення байта (модуль 64) може бути позначене як наголос рядка:

str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"

4

Потім робиться невелика математика для узгодження модуля на основі значення в $b, перший байт в $aостаточно позбавлений, а вихідний для поточного циклу додається до стека до завершення перед циклом переробляється, щоб перевірити, чи $aдійсно порожній.

    eval "   unset   LC_ALL  a b c;${2%?})'\''"
    return  "$((${OPTARG%%\**}-1))"

Коли $aнапевно порожньо, всі імена та стан - за винятком $OPTARG- що функція, на яку впливає протягом усього її виконання, відновлюється до попереднього стану - незалежно від того, встановлена ​​чи не null, set і null, або unset - і вихід зберігається до $OPTARGповернення функції. Фактичне значення повернення на одиницю менше, ніж загальна кількість байтів у першому символі його першого аргументу - тому будь-який символ одного байта повертає нуль, а будь-який багатобайтовий знак поверне більше нуля - і його вихідний формат трохи дивний.

Значення ctbl()економить $OPTARGє допустимим оболонки арифметичне вираз , яке, якщо оцінені, буде одночасно встановити імена змінних форм $o1, $d1, $o2, $d2в десяткової і вісімковій значень всіх відповідних байтів в першому символі перший аргумент, але в кінцевому рахунку оцінити в загальній складності кількість байт у своєму першому аргументі. Я мав на увазі певний вид робочого процесу, коли писав це, і я думаю, може бути демонстрація в порядку.

Я часто знаходжу причину роз'єднувати рядок з getoptsподібними:

str=some\ string OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done

s
o
m
e

s
t
r
i
n
g

Я, мабуть, роблю трохи більше, ніж просто роздруковую це за графіку, але все можливо. У будь-якому випадку, я ще не знайшов , getoptsщо буде правильно робити (удар, - dash«S getoptsробить його обвуглюється на гольця, але , bashбезумовно , не чинить) :

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş  OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done|   od -tc

0000000 305  \n 220  \n 305  \n 221  \n 305  \n 222  \n 305  \n 223  \n
0000020 305  \n 224  \n 305  \n 225  \n 305  \n 226  \n 305  \n 227  \n
0000040 305  \n 230  \n 305  \n 231  \n 305  \n 232  \n 305  \n 233  \n
0000060 305  \n 234  \n 305  \n 235  \n 305  \n 236  \n 305  \n 237  \n
0000100

Гаразд. Тому я спробував ...

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş
while   [ 0 -ne "${#str}" ]
do      printf %c\\n "$str"    #identical results for %.1s
        str=${str#?}
done|   od -tc

#dash
0000000 305  \n 220  \n 305  \n 221  \n 305  \n 222  \n 305  \n 223  \n
0000020 305  \n 224  \n 305  \n 225  \n 305  \n 226  \n 305  \n 227  \n
0000040 305  \n 230  \n 305  \n 231  \n 305  \n 232  \n 305  \n 233  \n
0000060 305  \n 234  \n 305  \n 235  \n 305  \n 236  \n 305  \n 237  \n
0000100

#bash
0000000 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n
*
0000040

Такий робочий процес - байт для байтів / char для типу char - той, до якого я часто потрапляю, роблячи невеликі речі. На передньому краї введення вам потрібно знати значення знаків, як тільки ви їх читаєте, і вам потрібні їх розміри (особливо під час підрахунку стовпців) , і вам потрібні символи, щоб вони були цілими символами.

І ось тепер у мене є ctbl():

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş
while [ 0 -ne "${#str}" ]
do    ctbl "$str"
      printf "%.$(($OPTARG))s\t::\t$OPTARG\t::\t$?\t::\t\\$o1\\$o2\n" "$str"
      str=${str#?}
done

Ő   ::  2*((o1=305)>=(d1=197)|(o2=220)>=(d2=144))   ::  1   ::  Ő
ő   ::  2*((o1=305)>=(d1=197)|(o2=221)>=(d2=145))   ::  1   ::  ő
Œ   ::  2*((o1=305)>=(d1=197)|(o2=222)>=(d2=146))   ::  1   ::  Œ
œ   ::  2*((o1=305)>=(d1=197)|(o2=223)>=(d2=147))   ::  1   ::  œ
Ŕ   ::  2*((o1=305)>=(d1=197)|(o2=224)>=(d2=148))   ::  1   ::  Ŕ
ŕ   ::  2*((o1=305)>=(d1=197)|(o2=225)>=(d2=149))   ::  1   ::  ŕ
Ŗ   ::  2*((o1=305)>=(d1=197)|(o2=226)>=(d2=150))   ::  1   ::  Ŗ
ŗ   ::  2*((o1=305)>=(d1=197)|(o2=227)>=(d2=151))   ::  1   ::  ŗ
Ř   ::  2*((o1=305)>=(d1=197)|(o2=230)>=(d2=152))   ::  1   ::  Ř
ř   ::  2*((o1=305)>=(d1=197)|(o2=231)>=(d2=153))   ::  1   ::  ř
Ś   ::  2*((o1=305)>=(d1=197)|(o2=232)>=(d2=154))   ::  1   ::  Ś
ś   ::  2*((o1=305)>=(d1=197)|(o2=233)>=(d2=155))   ::  1   ::  ś
Ŝ   ::  2*((o1=305)>=(d1=197)|(o2=234)>=(d2=156))   ::  1   ::  Ŝ
ŝ   ::  2*((o1=305)>=(d1=197)|(o2=235)>=(d2=157))   ::  1   ::  ŝ
Ş   ::  2*((o1=305)>=(d1=197)|(o2=236)>=(d2=158))   ::  1   ::  Ş
ş   ::  2*((o1=305)>=(d1=197)|(o2=237)>=(d2=159))   ::  1   ::  ş

Зауважте, що ctbl()насправді не визначають $[od][12...]змінні - вона ніколи не має довготривалого впливу на будь-який стан, але $OPTARG- лише вкладає рядок, $OPTARGякий можна використовувати для їх визначення - саме тому я отримую другу копію кожної таблиці вище, роблячи, printf "\\$o1\\$o2"тому що вони встановлюються щоразу, коли я оцінюю $(($OPTARG)). Але де я це роблю я також оголосити довжину поля модифікатор printf«s %sформат рядка аргументу, і тому , що вираз завжди обчислюється на загальне число байтів в характері, я отримую весь характер на виході , коли я роблю:

printf %.2s "$str"

Ви повинні змагатись у прихованому конкурсі баш-коду!
HelloGoodbye

1
@HelloGoodbye це не баш-код . і це не затуманено. щоб побачити примхливість, будь-ласка, зверніться до цього питання [ "$(printf \\1)" ]|| ! echo but its not null!, сміливо ознайомтеся зі змістовною практикою коментування, якщо ви не рекомендуєте фактичний такий конкурс ...?
mikeserv

Ні, я не знаю, те, що я написав, був просто іншим способом сказати, що ваш код дуже заплутаний (принаймні для мене), але, можливо, це не повинно бути легко зрозумілим. Якщо це не баш, то яка мова?
HelloGoodbye

@HelloGoodbye - це shкомандна мова POSIX . bashце знову буржуазний набір того ж самого, і значною мірою - невід'ємний мотиватор для більшої частини турбот, наданих вище до широко портативних, саморозширюваних та просторів імен, почесних розмірів символів будь-якого типу. bashВи повинні вже вирішити багато з цього, але cмова printfбула, і, можливо, є недостатньою для вищезазначених можливостей.
mikeserv

Я все ще схильний використовувати printf "% d" "'$ char" заради простоти та читабельності. Мені цікаво, з якими проблемами це ставить мене на адресу рішення @ mikeserv? Чи є більше, ніж лише деякі контрольні символи, що впливають на код повернення (що, на мою думку, було його суттю у вищезазначеному коментарі)?
Алекс Янсен

3

Не сценарій оболонки, але працює

awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'  

Вибірка зразка

xieerqi:$ awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }' | head -n 5                                    
a 97
b 98
c 99
d 100
e 101

2
  • виберіть символ, потім натисніть CTRL + C
  • відчинено konsole
  • та введіть: xxd<press enter>
  • потім натисніть <SHIFT+INSERT><CTRL+D>

ви отримуєте щось на кшталт:

mariank@dd903c5n1 ~ $ xxd
û0000000: fb 

ви знаєте, що символ, який ви вставили, має шістнадцятковий код 0xfb

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.