Bash script untuk mendapatkan nilai ASCII untuk alfabet


Jawaban:


70

Tentukan dua fungsi ini (biasanya tersedia dalam bahasa lain):

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

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

Pemakaian:

chr 65
A

ord A
65

7
@ dmsk80: +1. Bagi orang lain seperti saya yang berpikir mereka melihat salah ketik: "'A"benar sedangkan jika Anda menggunakan "A"ia akan berkata: A: invalid number. Tampaknya itu dilakukan pada sisi printf (yaitu, di shell, "'A"memang 2 karakter, a 'dan a A. Mereka dilewatkan ke printf. Dan dalam konteks printf, itu dikonversi ke nilai ascii dari A, (dan akhirnya dicetak sebagai desimal berkat '%d'. Gunakan 'Ox%x'untuk menunjukkannya dalam heksa atau '0%o'memilikinya dalam oktal))
Olivier Dulac

3
-1 karena tidak menjelaskan cara kerjanya ... bercanda: D, tapi serius apa yang ini printf "\\$(printf '%03o' "$1")", '%03o', LC_CTYPE=Cdan kutipan tunggal di "'$1"do?
razzak

1
Baca semua detail di FAQ 71 . Analisis detail yang sangat baik.

19

Anda dapat melihat seluruh rangkaian dengan:

$ man ascii

Anda akan mendapatkan tabel dalam oktal, hex, dan desimal.


Ada juga paket ascii untuk distro berbasis debian, tetapi (setidaknya sekarang) pertanyaan tersebut ditandai sebagai bash, jadi ini tidak akan membantu OP. Bahkan, itu diinstal pada sistem saya dan semua yang saya dapatkan dari man ascii adalah halaman manualnya.
Joe

12

Jika Anda ingin memperluasnya ke karakter UTF-8:

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

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

Dengan bash, kshatau zshbawaan:

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

Apakah Anda bermaksud untuk meletakkan karakter kotak persegi atau yang lain maka karakter asli tidak ditampilkan di pos dan digantikan oleh karakter kotak persegi.
mtk

1
@ mtk, Anda memerlukan browser yang menampilkan UTF-8 dan font yang memiliki karakter 128520 .
Stéphane Chazelas

Saya menggunakan Chrome Terbaru, dan tidak berpikir bahwa itu tidak mendukung UTF-8. Ingin tahu browser apa yang Anda gunakan?
mtk

@ mtk, iceweaselaktif Debian sid. Font yang dikonfirmasi oleh konsol web iceweasel adalah "DejaVu Sans" dan saya punya ttf-dejavu ttf-dejavu-core ttf-dejavu-paket tambahan yang diinstal yang berasal dari Debian dengan upstream di dejavu-fonts.org
Stéphane Chazelas

apa dasar dari 128520? saya sendiri ctbl()tampaknya benar memungkinkan saya untuk menampilkannya, dan untuk mengiris char dari kepala string dengan printf, tetapi menempatkan 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))di $OPTARGuntuk nilai byte.
mikeserv

12

Ini bekerja dengan baik,

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.

persis sama dengan:

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

3
Bisakah Anda menambahkan sedikit penjelasan?
Bernhard

tr untuk menghapus "\ n" (baris baru) dari input. od digunakan untuk -t dC adalah untuk mencetak dalam karakter desimal.
Saravanan

1
echo -nmenekan trailing newline menghilangkan kebutuhan untuktr -d "\n"
Gowtham

2
@ Powtham, hanya dengan beberapa implementasi echo, tidak dalam gema yang sesuai dengan Unix misalnya. printf %s Aakan menjadi yang portabel.
Stéphane Chazelas

6

Saya akan mencari solusi Bash yang sederhana (dan elegan?):

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

Untuk dalam skrip Anda dapat menggunakan yang berikut:

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

Perhatikan kutipan tunggal sebelum CharValue. Itu wajib ...


1
Bagaimana jawaban Anda berbeda dari jawaban dsmsk80?
Bernhard

1
Interpretasi saya atas pertanyaan adalah "bagaimana cara mendapatkan nilai ASCII untuk nilai-nilai alfabet". Bukan cara mendefinisikan fungsi untuk mengambil nilai ASCII untuk satu karakter. Jadi jawaban pertama saya adalah perintah satu baris pendek untuk mendapatkan nilai ASCII untuk alfabet.
phulstaert

Saya mengerti maksud Anda, tetapi saya masih berpikir bahwa intinya dari kedua jawaban itu printf "%d".
Bernhard

2
Saya setuju ini adalah bagian penting dari proses untuk sampai ke hasil, namun saya tidak ingin membuat asumsi bahwa xmpirate tahu tentang "untuk saya dalam" dan penggunaan rentang. Jika dia menginginkan daftar, ini bisa menjadi penghemat waktu nyata ;-). Selain itu, pembaca di masa mendatang mungkin menganggap bahwa penambahan saya bermanfaat.
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))"
}'

Yang pertama ctbl()- di atas sana - hanya berjalan satu kali. Ini menghasilkan output berikut (yang telah difilter sed -n ldemi printability) :

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"$

... yang semuanya 8-bit byte (kurang NUL) , dibagi menjadi empat string yang dikutip shell dibagi secara merata pada batas 64-byte. String mungkin diwakili dengan rentang oktal suka \200\1-\77, \100-\177, \200-\277, \300-\377, di mana byte 128 digunakan sebagai tempat pemegang untuk NUL.

Pertama ctbl()'s seluruh tujuan untuk keberadaan adalah untuk menghasilkan string tersebut sehingga evaldapat menentukan kedua ctbl()fungsi dengan mereka secara harfiah tertanam setelahnya. Dengan cara itu mereka dapat direferensikan dalam fungsi tanpa perlu membuatnya lagi setiap kali dibutuhkan. Ketika evalmendefinisikan ctbl()fungsi kedua , yang pertama akan berhenti.

Setengah bagian atas dari ctbl()fungsi kedua sebagian besar tambahan di sini - ia dirancang untuk membuat serialisasi portabilitas shell dengan aman saat ini dan apa pun yang mungkin mempengaruhi ketika dipanggil. Loop atas akan mengutip setiap kutipan dalam nilai-nilai variabel apa pun yang mungkin ingin digunakan, dan kemudian menumpuk semua hasil dalam parameter posisinya.

Dua baris pertama, bagaimanapun, pertama segera mengembalikan 0 dan diatur $OPTARGke sama jika argumen pertama fungsi tidak mengandung setidaknya satu karakter. Dan jika ya, baris kedua segera memotong argumen pertamanya hanya karakter pertama - karena fungsi hanya menangani karakter pada suatu waktu. Yang penting, ia melakukan ini dalam konteks lokal saat ini, yang berarti bahwa jika karakter dapat terdiri lebih dari satu byte, maka, asalkan shell dengan benar mendukung multi-byte chars, ia tidak akan membuang byte kecuali kecuali yang tidak ada dalam karakter pertama dari argumen pertamanya.

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

Itu kemudian melakukan loop simpan jika diperlukan, dan setelah itu mendefinisikan kembali konteks lokal saat ini ke lokal C untuk setiap kategori dengan menetapkan LC_ALLvariabel. Dari titik ini, satu karakter hanya dapat terdiri dari satu byte, dan jadi jika ada beberapa byte dalam karakter pertama dari argumen pertama, ini sekarang harus dialamatkan masing-masing sebagai karakter individu dalam hak mereka sendiri.

        LC_ALL=C

Karena alasan inilah maka bagian kedua dari fungsi adalah while loop , sebagai lawan dari urutan berjalan tunggal. Dalam kebanyakan kasus, ia mungkin hanya akan mengeksekusi satu kali per panggilan, tetapi, jika shell yang ctbl()didefinisikan dengan benar menangani karakter multi-byte, itu mungkin loop.

        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)"'     "$@"

Perhatikan bahwa $(ctbl)substitusi perintah di atas hanya pernah dievaluasi sekali - pada evalsaat fungsi awalnya didefinisikan - dan bahwa selamanya setelah itu token diganti dengan output literal dari substitusi perintah yang disimpan ke dalam memori shell. Hal yang sama berlaku untuk dua casepergantian perintah pola. Fungsi ini tidak pernah memanggil subkulit atau perintah lainnya. Ini juga tidak akan pernah mencoba untuk membaca atau menulis input / output (kecuali dalam kasus beberapa pesan diagnostik shell - yang mungkin menunjukkan bug) .

Perhatikan juga bahwa tes untuk kontinuitas loop tidak hanya [ -n "$a" ], karena, seperti yang saya temukan pada frustasi saya, untuk beberapa alasan bashshell tidak:

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

but it's not null!

... dan jadi saya secara eksplisit membandingkan $alen ke 0 untuk setiap iterasi, yang, juga tidak dapat dijelaskan, berperilaku berbeda (baca: dengan benar) .

The casecek byte pertama untuk dimasukkan dalam salah satu dari empat senar dan toko referensi ke set byte dalam $b. Setelah itu, empat parameter posisi pertama shell adalah setstring yang disematkan evaldan ditulis oleh ctbl()pendahulunya.

Selanjutnya, apa pun yang tersisa dari argumen pertama sekali lagi terpotong sementara untuk karakter pertamanya - yang sekarang harus dipastikan sebagai byte tunggal. Byte pertama ini digunakan sebagai referensi untuk melepas dari ujung string yang cocok dan referensi di $badalah evaluntuk mewakili parameter posisi sehingga segala sesuatu dari byte referensi ke byte terakhir dalam string dapat diganti. Tiga string lainnya dijatuhkan sepenuhnya dari parameter posisi.

               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)))|"

Pada titik ini nilai byte (modulo 64) dapat dirujuk sebagai len string:

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

4

Sedikit matematika kemudian dilakukan untuk mendamaikan modulus berdasarkan nilai dalam $b, byte pertama in $asecara permanen dihapus, dan output untuk siklus saat ini ditambahkan ke tumpukan sambil menunggu penyelesaian sebelum loop daur ulang untuk memeriksa apakah $abenar-benar kosong.

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

Ketika $abenar-benar kosong, semua nama dan negara - dengan pengecualian $OPTARG- bahwa fungsi yang terpengaruh selama pelaksanaannya dikembalikan ke keadaan sebelumnya - apakah disetel dan bukan nol, setel dan batal, atau tidak disetel - dan hasilnya disimpan untuk $OPTARGsebagai fungsi kembali. Nilai pengembalian aktual adalah satu kurang dari jumlah total byte dalam karakter pertama dari argumen pertamanya - sehingga setiap karakter byte tunggal mengembalikan nol dan karakter multi-byte akan mengembalikan lebih dari nol - dan format outputnya sedikit aneh.

Nilai ctbl()menghemat untuk $OPTARGadalah shell ekspresi aritmatika valid yang, jika dievaluasi, bersamaan akan mengatur nama variabel bentuk $o1, $d1, $o2, $d2untuk desimal dan nilai-nilai oktal dari semua byte masing-masing dalam karakter pertama dari argumen pertama, tapi akhirnya mengevaluasi total jumlah byte dalam argumen pertamanya. Saya memiliki jenis alur kerja tertentu dalam pikiran ketika menulis ini, dan saya pikir mungkin demonstrasi dalam rangka.

Saya sering menemukan alasan untuk memisahkan string dengan getoptsseperti:

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

Saya mungkin melakukan sedikit lebih dari sekedar mencetaknya char per baris, tapi semuanya mungkin. Dalam hal apapun, saya belum menemukan getoptsyang benar akan melakukan (strike itu - dash's getoptsmelakukannya char by char, tapi bashpasti tidak) :

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

Baik. Jadi saya mencoba ...

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

Alur kerja semacam itu - byte untuk byte / char untuk jenis char - adalah salah satu yang sering saya bahas ketika melakukan hal-hal tty. Di ujung depan input, Anda perlu mengetahui nilai char segera setelah Anda membacanya, dan Anda perlu ukurannya (terutama ketika menghitung kolom) , dan Anda perlu karakter untuk menjadi karakter keseluruhan .

Dan sekarang saya punya 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   ::  ş

Catatan yang ctbl()tidak benar-benar mendefinisikan $[od][12...]variabel - tidak pernah memiliki efek abadi pada keadaan apa pun tetapi $OPTARG- tetapi hanya menempatkan string $OPTARGyang dapat digunakan untuk mendefinisikan mereka - yang adalah bagaimana saya mendapatkan salinan kedua dari masing-masing karakter di atas dengan melakukan printf "\\$o1\\$o2"karena mereka diset setiap kali saya mengevaluasi $(($OPTARG)). Tapi di mana saya melakukannya saya juga mendeklarasikan panjang lapangan pengubah untuk printf's %sFormat argumen string, dan karena ekspresi selalu mengevaluasi dengan jumlah total byte dalam karakter, saya mendapatkan seluruh karakter pada output ketika saya lakukan:

printf %.2s "$str"

Anda harus bersaing dalam kontes kode bash yang dikaburkan!
HelloGoodbye

1
@ Halo Selamat Tinggal ini bukan kode bash . ini juga tidak dikaburkan. untuk melihat kebingungan, silakan merujuk [ "$(printf \\1)" ]|| ! echo but its not null!sementara itu, merasa bebas untuk lebih mengenal diri sendiri dengan praktik komentar yang bermakna, kecuali jika Anda merekomendasikan kontes yang sebenarnya ...?
mikeserv

Tidak, saya tidak, apa yang saya tulis hanyalah cara lain untuk mengatakan bahwa kode Anda sangat membingungkan (setidaknya bagi saya), tapi mungkin itu tidak seharusnya mudah dimengerti. Jika bukan bash, lalu bahasa apa itu?
HelloGoodbye

@ HaloGoodbye - ini adalah shbahasa perintah POSIX . bashadalah bourne lagi supraset yang sama, dan sebagian besar motivator yang terjal untuk banyak perawatan yang diberikan di atas menuju ukuran karakter yang luas portabel, memperluas diri dan namespace jenis apa pun. bashharus menangani sebagian besar dari ini, tetapi cbahasa printfitu, dan mungkin, kekurangan kemampuan di atas disediakan.
mikeserv

Saya masih cenderung menggunakan printf "% d" "'$ char" demi kesederhanaan dan keterbacaan. Saya ingin tahu masalah apa yang membuat saya terpapar pada alamat solusi @ mikeserv? Apakah ada lebih dari beberapa karakter kontrol yang memengaruhi kode pengembalian (yang saya yakin adalah poinnya dalam komentar di atas)?
Alex Jansen

3

Bukan skrip shell, tetapi berfungsi

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

Output sampel

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
  • pilih simbol, lalu tekan CTRL + C
  • Buka konsole
  • dan ketik: xxd<press enter>
  • lalu tekan <SHIFT+INSERT><CTRL+D>

Anda mendapatkan sesuatu seperti:

mariank@dd903c5n1 ~ $ xxd
û0000000: fb 

Anda tahu simbol yang Anda tempel memiliki kode hex 0xfb

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.