Script Bash pour obtenir les valeurs ASCII pour l'alphabet


Réponses:


70

Définissez ces deux fonctions (généralement disponibles dans d'autres langues):

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

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

Usage:

chr 65
A

ord A
65

7
@ dmsk80: +1. Pour d' autres comme moi qui pensent qu'ils repèrent une faute de frappe: "'A"est correcte alors que si vous utilisez "A"ce dira: A: invalid number. Il semble que cela soit fait du côté de printf (c’est-à-dire que, dans le shell, il "'A"y a bien 2 caractères, a 'et a A. Ceux-ci sont passés à printf. Et dans le contexte de printf, il est converti en valeur ascii de A, en décimal grâce à l ' '%d'. Utilisez - le 'Ox%x'pour l' afficher en hexa ou '0%o'pour l 'avoir en octal))
Olivier Dulac

3
-1 pour ne pas expliquer comment cela fonctionne ... en plaisantant: D, mais sérieusement qu'est-ce que ceux - ci printf "\\$(printf '%03o' "$1")", '%03o', LC_CTYPE=Cet la citation unique "'$1"do?
Razzak

1
Lisez tous les détails dans la FAQ 71 . Une excellente analyse détaillée.

19

Vous pouvez voir l'ensemble avec:

$ man ascii

Vous obtiendrez des tableaux en octal, hex et décimal.


Il existe également un paquet ascii pour les distributions basées sur Debian, mais (au moins maintenant), la question est étiquetée en tant que bash, donc cela n’aiderait pas le PO. En fait, il est installé sur mon système et tout ce que je tire de man ascii, c’est sa page de manuel.
Joe

12

Si vous souhaitez l'étendre aux caractères UTF-8:

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

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

Avec bash, kshou zshintégré:

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

Avez-vous l'intention de mettre un caractère de case carrée ou alors le caractère d'origine ne s'affiche pas dans le message et est remplacé par un caractère de case carrée.
MTK

1
@ mtk, vous avez besoin d'un navigateur affichant le format UTF-8 et d'une police contenant ce caractère 128520 .
Stéphane Chazelas

Je suis sur Latest Chrome et ne pense pas qu'il ne prend pas en charge UTF-8. Voudriez-vous savoir sur quel navigateur vous êtes?
MTK

@ MTK, iceweaselsur Debian sid. La police a confirmé que par la console Web de Iceweasel est "Sans DejaVu" et j'ai TTF-dejavu TTF-dejavu-core packages supplémentaires TTF-dejavu installés qui viennent de Debian avec en amont à dejavu-fonts.org
Stéphane Chazelas

quelle est la base de 128520? mon propre ctbl()semble me permettre de l' afficher correctement, et de couper l'omble chevalier de la tête d'une chaîne avec printf, mais il met 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))en $OPTARGpour les valeurs d'octets.
mikeserv

12

Ça marche bien,

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.

exactement équivalent à:

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

3
Pouvez-vous peut-être ajouter une petite explication?
Bernhard

tr pour supprimer "\ n" (nouvelle ligne) de l'entrée. od est utilisé pour -t dC consiste à imprimer en caractère décimal.
Saravanan

1
echo -nsupprime le retour à la ligne, éliminant ainsi le besoin detr -d "\n"
Gowtham

2
@Gowtham, uniquement avec certaines implémentations de echo, pas en échos compatibles Unix par exemple. printf %s Aserait le portable.
Stéphane Chazelas

6

Je vais pour la solution simple (et élégante?) Bash:

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

Dans un script, vous pouvez utiliser les éléments suivants:

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

Notez le guillemet simple avant CharValue. C'est obligé ...


1
En quoi votre réponse est-elle différente de celle de dsmsk80?
Bernhard

1
Mon interprétation de la question est "comment obtenir les valeurs ASCII pour les valeurs de l'alphabet". Pas comment définir une fonction pour récupérer la valeur ASCII pour un caractère. Ma première réponse est donc une courte commande d’une ligne pour obtenir les valeurs ASCII de l’alphabet.
phulstaert

Je comprends ce que vous voulez dire, mais je pense toujours que l’essentiel des deux réponses est printf "%d".
Bernhard

2
Je conviens que ceci est une partie cruciale du processus pour arriver au résultat, mais je ne voulais pas faire l'hypothèse que xmpirate était au courant du "for i in" et de l'utilisation d'une plage. S'il voulait une liste, cela pourrait faire gagner beaucoup de temps ;-). De plus, les futurs lecteurs pourraient trouver mes ajouts utiles.
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))"
}'

Le premier ctbl()- au sommet là-bas - ne court jamais qu'une seule fois. Il génère la sortie suivante (qui a été filtrée à des sed -n lfins d’imprimabilité) :

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

... qui sont tous des octets de 8 bits (moins NUL) , divisés en quatre chaînes citées par le shell, divisées de manière égale à des limites de 64 octets. Les chaînes peuvent être représentées avec des gammes octal aiment \200\1-\77, \100-\177, \200-\277, \300-\377où l' octet 128 est utilisé comme lieu porte pour NUL.

Le but premier de ctbl()la première existence est de générer ces chaînes de manière à evalpouvoir définir la seconde ctbl()fonction avec elles incorporées littéralement par la suite. De cette manière, ils peuvent être référencés dans la fonction sans avoir à les générer à chaque fois que nécessaire. Quand evaldéfinit-on la deuxième ctbl()fonction, la première cessera d'être.

La moitié supérieure de la seconde ctbl()fonction est principalement auxiliaire ici - elle est conçue pour sérialiser de manière portable et en toute sécurité tout état de coque actuel qu'il pourrait affecter lors de son appel. La boucle supérieure citera les guillemets dans les valeurs des variables qu’elle voudra utiliser, puis empilera tous les résultats dans ses paramètres de position.

Les deux premières lignes, cependant, retournent immédiatement immédiatement 0 et sont $OPTARGidentiques si le premier argument de la fonction ne contient pas au moins un caractère. Et si tel est le cas, la deuxième ligne tronque immédiatement son premier argument en son premier caractère uniquement, car la fonction ne traite qu'un caractère à la fois. Il est important de noter que cela se produit dans le contexte local actuel, ce qui signifie que si un caractère peut comprendre plus d’un octet, le shell supportera correctement les caractères multi-octets, il ne supprimera aucun octet, à l’exception de ceux qui ne figurent pas dans la liste. premier caractère de son premier argument.

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

Il effectue ensuite la boucle de sauvegarde si nécessaire, puis redéfinit le contexte local en cours en fonction du paramètre régional C pour chaque catégorie en lui affectant une LC_ALLvariable. À partir de ce moment, un caractère ne peut être composé que d'un seul octet. Par conséquent, s'il y avait plusieurs octets dans le premier caractère de son premier argument, il devrait désormais s'agir chacun d'adresses adressables comme des caractères individuels à part entière.

        LC_ALL=C

C'est pour cette raison que la seconde moitié de la fonction est une while boucle , par opposition à une séquence à exécution unique. Dans la plupart des cas, il ne sera probablement exécuté qu'une seule fois par appel, mais si le shell dans lequel ctbl()est défini correctement gère les caractères multi-octets, il risque de se répéter.

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

Notez que la $(ctbl)substitution de commande ci-dessus n'est évaluée qu'une seule fois - evallorsque la fonction est définie pour la première fois - et que ce jeton est remplacé pour toujours par la sortie littérale de cette substitution de commande, telle qu'elle est enregistrée dans la mémoire du shell. Il en va de même pour les casesubstitutions de commande de deux motifs. Cette fonction n'appelle jamais un sous-shell ou une autre commande. Il ne tentera jamais non plus de lire ou d’écrire des entrées / sorties (sauf dans le cas d’un message de diagnostic du shell - qui indique probablement un bogue) .

Notez également que le test de la continuité de la boucle n’est pas simplement [ -n "$a" ], car, comme j’ai trouvé ma frustration, pour une raison quelconque, un bashshell ne:

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

but it's not null!

... et je compare donc explicitement $alen à 0 pour chaque itération, ce qui, de manière inexplicable, se comporte différemment (lire: correctement) .

Le casevérifie si le premier octet est inclus dans l’une de nos quatre chaînes et stocke une référence à l’octet défini dans $b. Ensuite, les quatre premiers paramètres de position du shell setconcernent les chaînes incorporées evalet écrites par ctbl()le prédécesseur de.

Ensuite, tout ce qui reste du premier argument est de nouveau temporairement tronqué au premier caractère - qui devrait maintenant être assuré d'être un simple octet. Ce premier octet est utilisé comme référence pour extraire la fin de la chaîne à laquelle il correspond et que la référence $best eval'd pour représenter un paramètre de position afin que tout, de l'octet de référence au dernier octet de la chaîne, puisse être remplacé. Les trois autres chaînes sont entièrement supprimées des paramètres de position.

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

À ce stade, la valeur de l'octet (modulo 64) peut être référencée comme len de la chaîne:

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

4

Un peu de calcul est ensuite effectué pour réconcilier le module en fonction de la valeur entrée $b, le premier octet entré $aest supprimé de façon permanente et la sortie du cycle en cours est ajoutée à une pile en attente d’achèvement avant que la boucle ne soit recyclée pour vérifier si elle $aest réellement vide.

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

Lorsqu'il $aest définitivement vide, tous les noms et tous les états, à l'exception de ce $OPTARGque la fonction affectée tout au long de son exécution, sont restaurés à leur état précédent (qu'ils soient définis et non nuls, définis et nuls ou non définis) et la sortie est enregistrée. pour $OPTARGque la fonction retourne. La valeur de retour réelle est inférieure au nombre total d'octets dans le premier caractère de son premier argument - ainsi, tout caractère sur un octet renvoie zéro et tout caractère multi-octets renverra plus que zéro - et son format de sortie est un peu étrange.

La valeur ctbl()sauve à $OPTARGun shell valide expression arithmétique que, si une évaluation, seront les noms de variables mis en même temps que des formes $o1, $d1, $o2, $d2à valeurs décimales et octal de tous les octets respectifs dans le premier caractère de son premier argument, mais finalement évaluer au total nombre d'octets dans son premier argument. J'avais un type de flux de travail spécifique à l'esprit lorsque j'ai écrit ceci, et je pense qu'une démonstration est peut-être nécessaire.

Je trouve souvent une raison de séparer une chaîne de caractères avec 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

Je fais probablement un peu plus que simplement imprimer un caractère par ligne, mais tout est possible. Dans tous les cas, je ne l' ai pas encore trouvé getoptsqui bien faire (grève - dash« est le getoptsfait carboniser par char, mais bashne certainement pas) :

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

D'accord. Alors j'ai essayé ...

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

Ce type de flux de travail - l'octet pour octet / caractère pour caractère de caractère - est celui dans lequel je m'engage souvent lorsque je fais des choses difficiles. Au début de la saisie, vous devez connaître les valeurs des caractères dès que vous les lisez et leur taille (en particulier lors du comptage de colonnes) , ainsi que des caractères entiers .

Et maintenant j'ai 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   ::  ş

Notez que ctbl()cela ne définit pas réellement les $[od][12...]variables - cela n’a aucun effet durable sur un état, mais $OPTARG- mais ne met que la chaîne $OPTARGqui peut être utilisée pour les définir - c’est ainsi que je récupère la deuxième copie de chaque caractère ci-dessus printf "\\$o1\\$o2"parce que ils sont définis chaque fois que j'évalue $(($OPTARG)). Mais là où je le fais je déclare également un modificateur de longueur du champ à printfl » %sformat argument de chaîne, et parce que l'expression évalue toujours au nombre total d'octets dans un caractère, je reçois tout le caractère de la sortie quand je fais:

printf %.2s "$str"

Vous devriez participer à un concours de code bash obscurci!
HelloGoodbye

1
@ BonjourGoodbye ce n'est pas le code bash . ce n'est pas obscurci. en [ "$(printf \\1)" ]|| ! echo but its not null!attendant , veuillez vous reporter à la question , n'hésitez pas à mieux vous familiariser avec la pratique des commentaires significatifs, à moins que vous ne recommandiez un tel concours ...?
mikeserv

Non, ce que j'ai écrit était simplement une autre façon de dire que votre code est très déroutant (du moins pour moi), mais peut-être n'était-il pas censé être facilement compréhensible. Si ce n'est pas bash, alors quelle langue est-ce?
HelloGoodbye

@ HelloGoodbye - c'est le shlangage de commande POSIX . bashest encore une fois un élément suprême de la même chose, et en grande partie un facteur de motivation précipité pour la plupart des soins apportés ci-dessus à des tailles de caractère largement portables, expansibles et honorables en espace de nommage de toute sorte. bashdevrait en gérer une grande partie déjà, mais le clangage printfétait, et est peut-être, déficient de la capacité fournie ci-dessus.
mikeserv

Je suis toujours enclin à utiliser printf "% d" "'$ char" par souci de simplicité et de lisibilité. Je suis curieux de savoir quel genre de problèmes cela m'expose aux solutions proposées par @ mikeserv? Y a-t-il plus que quelques caractères de contrôle affectant le code de retour (ce qui, à mon avis, était dans le commentaire ci-dessus)?
Alex Jansen

3

Pas un script shell, mais fonctionne

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

Échantillon de sortie

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
  • sélectionnez le symbole, puis appuyez sur CTRL + C
  • ouvert konsole
  • et type: xxd<press enter>
  • puis appuyez <SHIFT+INSERT><CTRL+D>

vous obtenez quelque chose comme:

mariank@dd903c5n1 ~ $ xxd
û0000000: fb 

vous connaissez le symbole que vous avez collé a un code hexadécimal 0xfb

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.