Câu trả lời:
Xác định hai chức năng này (thường có sẵn trong các ngôn ngữ khác):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Sử dụng:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
và báo giá duy nhất trong "'$1"
việc phải làm?
Bạn có thể xem toàn bộ tập hợp với:
$ man ascii
Bạn sẽ nhận được các bảng theo số bát phân, thập lục phân và thập phân.
Nếu bạn muốn mở rộng nó thành các ký tự UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Với bash
, ksh
hoặc zsh
nội dung:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
trên Debian sid
. Font như khẳng định bởi giao diện điều khiển web iceweasel là "DejaVu Sans" và tôi đã có ttf-DejaVu ttf-DejaVu lõi ttf-DejaVu-thêm gói cài đặt mà đến từ Debian với thượng nguồn tại dejavu-fonts.org
Điều này hoạt động tốt,
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.
chính xác tương đương với:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
ngăn chặn dòng mới theo dõi loại bỏ sự cần thiết chotr -d "\n"
echo
, không phải trong tiếng vang tương thích Unix chẳng hạn. printf %s A
sẽ là một trong những di động.
Tôi sẽ dùng giải pháp Bash đơn giản (và thanh lịch?):
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Đối với một tập lệnh, bạn có thể sử dụng như sau:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Lưu ý trích dẫn duy nhất trước CharValue. Đó là nghĩa vụ ...
printf "%d"
.
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))"
}'
Cái đầu tiên ctbl()
- ở trên cùng - chỉ chạy một lần duy nhất. Nó tạo ra đầu ra sau (đã được lọc qua sed -n l
vì mục đích in) :
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"$
... Đó là tất cả các byte 8 bit (ít hơn NUL
) , được chia thành bốn chuỗi trích dẫn shell được chia đều ở các ranh giới 64 byte. Các chuỗi có thể được đại diện với dãy bát phân thích \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, nơi byte 128 được sử dụng như là một nơi-giữ cho NUL
.
ctbl()
Toàn bộ mục đích tồn tại đầu tiên là tạo ra các chuỗi đó để eval
có thể xác định ctbl()
hàm thứ hai với chúng được nhúng theo nghĩa đen sau đó. Theo cách đó, chúng có thể được tham chiếu trong hàm mà không cần tạo lại chúng mỗi khi chúng cần. Khi eval
nào xác định ctbl()
hàm thứ hai thì hàm thứ nhất sẽ hết.
Nửa trên của ctbl()
chức năng thứ hai chủ yếu là phụ trợ ở đây - nó được thiết kế để tuần tự hóa một cách hợp lý và an toàn bất kỳ trạng thái vỏ hiện tại nào mà nó có thể ảnh hưởng khi được gọi. Vòng lặp trên cùng sẽ trích dẫn bất kỳ dấu ngoặc kép nào trong các giá trị của bất kỳ biến nào mà nó có thể muốn sử dụng và sau đó xếp chồng tất cả các kết quả vào các tham số vị trí của nó.
Tuy nhiên, hai dòng đầu tiên ngay lập tức trả về 0 và được đặt $OPTARG
thành giống nhau nếu đối số đầu tiên của hàm không chứa ít nhất một ký tự. Và nếu có, dòng thứ hai ngay lập tức cắt bỏ đối số đầu tiên của nó thành ký tự đầu tiên - bởi vì hàm chỉ xử lý một ký tự tại một thời điểm. Điều quan trọng là, nó thực hiện điều này trong ngữ cảnh miền địa phương hiện tại, điều đó có nghĩa là nếu một ký tự có thể chứa nhiều hơn một byte, thì, với điều kiện shell hỗ trợ đúng các ký tự nhiều byte, nó sẽ không loại bỏ bất kỳ byte nào ngoại trừ các byte không có trong nhân vật đầu tiên của đối số đầu tiên của nó.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Sau đó, nó thực hiện vòng lặp lưu nếu cần thiết và sau đó nó xác định lại bối cảnh miền địa phương hiện tại thành miền địa phương C cho mọi danh mục bằng cách gán cho LC_ALL
biến. Từ thời điểm này, một ký tự chỉ có thể bao gồm một byte đơn, và vì vậy nếu có nhiều byte trong ký tự đầu tiên của đối số đầu tiên, thì các ký tự này sẽ có thể được định vị thành từng ký tự riêng lẻ.
LC_ALL=C
Vì lý do này mà nửa sau của hàm là một while
vòng lặp , trái ngược với chuỗi chạy đơn lẻ. Trong hầu hết các trường hợp, nó có thể sẽ chỉ thực hiện một lần cho mỗi cuộc gọi, nhưng, nếu shell ctbl()
được xác định đúng xử lý các ký tự nhiều byte, nó có thể lặp lại.
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)"' "$@"
Lưu ý rằng sự $(ctbl)
thay thế lệnh trên chỉ được đánh giá một lần - bởi eval
khi chức năng ban đầu được xác định - và mãi mãi sau khi mã thông báo đó được thay thế bằng đầu ra bằng chữ của sự thay thế lệnh đó được lưu vào bộ nhớ của shell. Điều tương tự cũng đúng với hai case
thay thế lệnh mẫu. Hàm này không bao giờ gọi một subshell hoặc bất kỳ lệnh nào khác. Nó cũng sẽ không bao giờ cố gắng đọc hoặc ghi đầu vào / đầu ra (trừ trường hợp một số thông báo chẩn đoán shell - có thể chỉ ra lỗi) .
Cũng lưu ý rằng kiểm tra tính liên tục của vòng lặp không chỉ đơn giản [ -n "$a" ]
, bởi vì, như tôi thấy với sự thất vọng của mình, vì một lý do nào đó, một bash
cái vỏ làm:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... Và vì vậy, tôi so sánh rõ ràng $a
len của 0 với mỗi lần lặp, trong đó, cũng không thể giải thích được, hoạt động khác nhau (đọc: chính xác) .
Các case
kiểm tra các byte đầu tiên để đưa vào một trong bốn dây và các cửa hàng của chúng tôi một tham chiếu đến bộ của byte trong $b
. Sau đó, bốn tham số vị trí đầu tiên của shell là set
các chuỗi được nhúng bởi eval
và được viết bởi ctbl()
người tiền nhiệm.
Tiếp theo, bất cứ thứ gì còn lại của đối số đầu tiên lại tạm thời bị cắt ngắn thành ký tự đầu tiên của nó - mà bây giờ sẽ được đảm bảo là một byte đơn. Byte đầu tiên này được sử dụng làm tham chiếu để tách khỏi đuôi của chuỗi mà nó khớp và tham chiếu trong $b
là eval
'd để biểu diễn một tham số vị trí để mọi thứ từ byte tham chiếu đến byte cuối cùng trong chuỗi có thể được thay thế. Ba chuỗi còn lại được loại bỏ hoàn toàn khỏi các tham số vị trí.
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)))|"
Tại thời điểm này, giá trị của byte (modulo 64) có thể được tham chiếu dưới dạng len của chuỗi:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Sau đó, một phép toán nhỏ được thực hiện để điều hòa mô đun dựa trên giá trị trong $b
, byte đầu tiên $a
bị loại bỏ vĩnh viễn và đầu ra cho chu kỳ hiện tại được nối vào ngăn xếp chờ hoàn thành trước khi vòng lặp tái chế để kiểm tra xem $a
có thực sự trống không.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Khi $a
chắc chắn là trống, tất cả các tên và trạng thái - ngoại trừ $OPTARG
- rằng hàm bị ảnh hưởng trong suốt quá trình thực thi của nó được khôi phục về trạng thái trước đó - cho dù được đặt và không null, đặt và null hoặc không đặt - và đầu ra được lưu để $OPTARG
như hàm trả về. Giá trị trả về thực tế nhỏ hơn một số tổng số byte trong ký tự đầu tiên của đối số đầu tiên của nó - vì vậy, bất kỳ ký tự byte đơn nào trả về 0 và bất kỳ char nhiều byte nào sẽ trả về nhiều hơn 0 - và định dạng đầu ra của nó hơi lạ.
Giá trị ctbl()
tiết kiệm đến $OPTARG
là một lớp vỏ số học biểu hiện giá trị đó, nếu đánh giá, đồng thời sẽ thiết lập tên biến trong các hình thức $o1
, $d1
, $o2
, $d2
để thập phân và giá trị bát phân của tất cả các byte tương ứng ở ký tự đầu tiên của các đối số đầu tiên của mình, nhưng cuối cùng đánh giá với tổng số số byte trong đối số đầu tiên của nó. Tôi đã có một loại quy trình công việc cụ thể trong tâm trí khi viết bài này, và tôi nghĩ có lẽ một cuộc biểu tình đã theo thứ tự.
Tôi thường tìm một lý do để tách một chuỗi getopts
như:
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
Tôi có thể làm nhiều hơn một chút so với chỉ in nó trên mỗi dòng, nhưng mọi thứ đều có thể. Trong mọi trường hợp, tôi vẫn chưa tìm thấy một getopts
mà đúng cách sẽ làm (đình công đó - dash
là getopts
làm nó char bởi char, nhưng bash
chắc chắn không) :
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
Đồng ý. Vì vậy, tôi đã cố gắng ...
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
Loại quy trình công việc đó - byte cho byte / char cho loại char - là công việc tôi thường gặp phải khi làm công việc tty. Ở cạnh đầu vào của đầu vào, bạn cần biết các giá trị char ngay khi bạn đọc chúng và bạn cần kích thước của chúng (đặc biệt là khi đếm các cột) và bạn cần các ký tự là toàn bộ ký tự.
Và bây giờ tôi có 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 :: ş
Lưu ý rằng ctbl()
không thực sự xác định các $[od][12...]
biến - nó không bao giờ có bất kỳ ảnh hưởng lâu dài nào đến bất kỳ trạng thái nào nhưng $OPTARG
- nhưng chỉ đặt chuỗi trong $OPTARG
đó có thể được sử dụng để xác định chúng - đó là cách tôi có được bản sao thứ hai của mỗi char ở trên bằng cách thực hiện printf "\\$o1\\$o2"
bởi vì chúng được đặt mỗi lần tôi đánh giá $(($OPTARG))
. Nhưng khi tôi làm điều đó, tôi cũng khai báo một công cụ sửa đổi độ dài trường printf
thành %s
định dạng đối số chuỗi và vì biểu thức luôn ước tính tổng số byte trong một ký tự, tôi nhận được toàn bộ ký tự khi xuất:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
trong khi đó, hãy thoải mái làm quen với thực hành bình luận có ý nghĩa, trừ khi bạn đề xuất một cuộc thi như vậy thực sự ...?
sh
ngôn ngữ lệnh POSIX . bash
là một bourne một lần nữa thay thế, và phần lớn là một động lực thúc đẩy cho phần lớn sự chăm sóc ở trên đối với các kích cỡ nhân vật đáng kính di động, tự mở rộng và không gian tên của bất kỳ loại nào. bash
nên xử lý nhiều vấn đề này, nhưng c
ngôn ngữ printf
đã và có thể là thiếu khả năng ở trên.
Không phải là một kịch bản shell, nhưng hoạt động
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Sản lượng mẫu
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
"'A"
là chính xác trong khi nếu bạn sử dụng"A"
nó sẽ nói :A: invalid number
. Có vẻ như nó được thực hiện ở phía printf (nghĩa là trong vỏ,"'A"
thực sự là 2 ký tự, a'
và aA
. Chúng được chuyển sang printf. Và trong bối cảnh printf, nó được chuyển đổi thành giá trị ascii của A, (và cuối cùng được in dưới dạng số thập phân nhờ vào'%d'
. Sử dụng'Ox%x'
để hiển thị bằng hexa hoặc'0%o'
để có số bát phân))