Comment obtenir la valeur ASCII de l'alphabet?
Par exemple, 97
pour a
?
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
Vous pouvez voir l'ensemble complet avec:
$ man ascii
Vous obtiendrez des tableaux en octal, hexadécimal et décimal.
Cela fonctionne 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'.
Si vous souhaitez l'étendre aux caractères UTF-8 (en supposant que vous êtes dans un environnement local UTF-8):
$ Perl -CA -le 'print ord shift' ????
128520
$ Perl -CS -le 'print chr shift' 128520
????
Avec bash
, ksh
ou zsh
intégrés:
$ printf "\U$(printf %08x 128520)\n"
????
J'opte pour la solution Bash simple (et élégante?):
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Car dans un script, vous pouvez utiliser ce qui suit:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Remarquez le devis unique avant CharValue. C'est obligatoire ...
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()
- en haut là-bas - ne s'exécute qu'une seule fois. Il génère la sortie suivante (qui a été filtrée à travers sed -n l
Pour l'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é en quatre chaînes entre guillemets réparties uniformément aux limites de 64 octets. Les chaînes peuvent être représentées avec des plages octales comme \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, où l'octet 128 est utilisé comme espace réservé pour NUL
.
Le premier but de l'existence de ctbl()
est de générer ces chaînes afin que eval
puisse définir la deuxième fonction ctbl()
avec elles littéralement incorporées par la suite. De cette façon, ils peuvent être référencés dans la fonction sans avoir besoin de les générer à nouveau chaque fois qu'ils sont nécessaires. Lorsque eval
définit la deuxième fonction ctbl()
, la première cesse d'être.
La moitié supérieure de la deuxième fonction ctbl()
est principalement accessoire ici - elle est conçue pour sérialiser de manière portative et en toute sécurité tout état Shell actuel qu'il pourrait affecter lors de son appel. La boucle supérieure cite toutes les citations dans les valeurs de toutes les variables qu'elle peut vouloir utiliser, puis empile tous les résultats dans ses paramètres de position.
Les deux premières lignes, cependant, retournent d'abord immédiatement 0 et définissent $OPTARG
Sur le même si le premier argument de la fonction ne contient pas au moins un caractère. Et si c'est le cas, la deuxième ligne tronque immédiatement son premier argument à son premier caractère uniquement, car la fonction ne gère qu'un caractère à la fois. Surtout, il le fait dans le contexte local actuel, ce qui signifie que si un caractère peut comprendre plus d'un seul octet, à condition que le Shell prenne correctement en charge les caractères multi-octets, il ne supprimera aucun octet, sauf ceux qui ne sont pas dans le premier caractère de son premier argument.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Il effectue ensuite la boucle de sauvegarde si cela est nécessaire, puis redéfinit le contexte de paramètres régionaux actuel en paramètres régionaux C pour chaque catégorie en attribuant à la variable LC_ALL
. À partir de ce moment, un caractère ne peut être composé que d'un seul octet, et donc s'il y avait plusieurs octets dans le premier caractère de son premier argument, ceux-ci devraient maintenant être chacun adressables en tant que caractères individuels à part entière.
LC_ALL=C
C'est pour cette raison que la seconde moitié de la fonction est un while
boucle, par opposition à une séquence exécutée individuellement. Dans la plupart des cas, il ne s'exécutera probablement qu'une seule fois par appel, mais si le shell dans lequel ctbl()
est correctement défini gère les caractères multi-octets, il pourrait boucle.
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 substitution de commande $(ctbl)
ci-dessus n'est évaluée qu'une seule fois - par eval
lorsque la fonction est initialement définie - et que pour toujours après ce jeton est remplacé par la sortie littérale de cette substitution de commande enregistrée dans la mémoire du Shell. Il en va de même pour les deux substitutions de commande de modèle case
. Cette fonction n'appelle jamais un sous-shell ou toute autre commande. Il n'essaiera pas non plus de lire ou d'écrire les entrées/sorties (sauf dans le cas de certains messages de diagnostic Shell - qui indiquent probablement un bogue).
Notez également que le test de continuité de boucle n'est pas simplement [ -n "$a" ]
, Car, comme je l'ai constaté à ma frustration, pour une raison quelconque, un shell bash
fait:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... et donc je compare explicitement le len de $a
à 0 pour chaque itération, qui, également inexplicablement, se comporte différemment (lire: correctement).
case
vérifie le premier octet à inclure dans l'une de nos quatre chaînes et stocke une référence à l'ensemble d'octets dans $b
. Ensuite, les quatre premiers paramètres positionnels du Shell sont set
dans les chaînes incorporées par eval
et écrites par le prédécesseur de ctbl()
.
Ensuite, tout ce qui reste du premier argument est à nouveau temporairement tronqué à son premier caractère - qui devrait maintenant être assuré d'être un seul octet. Ce premier octet est utilisé comme référence pour supprimer la queue de la chaîne à laquelle il correspond et la référence dans $b
Est eval
'd pour représenter un paramètre positionnel, donc tout, de l'octet de référence à le dernier octet de la chaîne peut ê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 le 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 dans $b
, Le premier octet de $a
Est définitivement supprimé et la sortie pour le cycle en cours est ajoutée à une pile en attendant la fin. avant que la boucle ne recycle pour vérifier si $a
est réellement vide.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Lorsque $a
Est définitivement vide, tous les noms et états - à l'exception de $OPTARG
- que la fonction affectée tout au long de son exécution sont restaurés à leur état précédent - qu'elle soit définie et non nulle, définie et nulle ou non définie - et la sortie est enregistrée dans $OPTARG
lorsque la fonction revient. La valeur de retour réelle est inférieure de un au nombre total d'octets dans le premier caractère de son premier argument - donc tout caractère à un octet renvoie zéro et tout caractère multi-octets retournera plus de zéro - et son format de sortie est un peu étrange.
La valeur que ctbl()
enregistre dans $OPTARG
Est une expression arithmétique Shell valide qui, si elle est évaluée, définira simultanément les noms de variables des formulaires $o1
, - $d1
, $o2
, $d2
aux valeurs décimales et octales de tous les octets respectifs dans le premier caractère de son premier argument, mais finalement évaluer le nombre total d'octets dans son premier argument. J'avais un type de flux de travail spécifique à l'esprit lorsque j'écris ceci, et je pense qu'une démonstration est peut-être nécessaire.
Je trouve souvent une raison de démonter une chaîne avec getopts
comme:
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 l'imprimer en caractères par ligne, mais tout est possible. En tout cas, je n'ai pas encore trouvé un getopts
qui fera correctement (biffez que - dash
est getopts
le fait char par char, mais bash
ne le fait 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. J'ai donc 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 l'octet/char pour le type char - est celui dans lequel j'entre souvent lorsque je fais des trucs tty. Au premier bord de l'entrée, vous devez connaître les valeurs de caractères dès que vous les lisez, et vous avez besoin de leurs tailles (en particulier lors du comptage des colonnes), et vous avez besoin que les caractères soient entier caractères.
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()
ne définit pas réellement les variables $[od][12...]
- il n'a jamais d'effet durable sur aucun état mais $OPTARG
- mais met seulement la chaîne dans $OPTARG
que peut être utilisé pour les définir - c'est ainsi que j'obtiens la deuxième copie de chaque caractère ci-dessus en faisant printf "\\$o1\\$o2"
car ils sont définis chaque fois que j'évalue $(($OPTARG))
. Mais là où je le fais, je déclare également un modificateur de longueur de champ au format d'argument de chaîne %s
De printf
, et parce que l'expression est toujours évaluée au nombre total d'octets dans un caractère, j'obtiens le caractère entier en sortie quand je fais:
printf %.2s "$str"
konsole
xxd<press enter>
<SHIFT+INSERT><CTRL+D>
vous obtenez quelque chose comme:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
vous savez que le symbole que vous avez collé a un code hexadécimal 0xfb
Pas un script Shell, mais fonctionne
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Exemple 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