labunix's blog

labunixのラボUnix

ASCIIコードの順序の性質と8進数エスケープの関係を整理してみる。

■ASCIIコードの順序の性質と8進数エスケープの関係を整理してみる。

 以下でASCIIコード表の8進数を一覧した。
 これを使って、検索パターンが意図した文字にマッチするかを確認する。

 awkで記号をエスケープしたいときに使う、文字と8進数の一覧
 http://labunix.hateblo.jp/entry/20180801/1533056689

■行処理(改行)から列処理(スペース)に変える。
 後々面倒なので、ブレース展開に書き換える。

$ echo "`seq 32 126`" | awk '{printf "%c,\\%03o\n",$1,$1}' > 1.txt

$ echo `seq 32 126` | awk '{for(a=1;a<=NF;a++)printf "%c,\\%03o\n",$a,$a}' > 2.txt
$ echo {32..126} | awk '{for(a=1;a<=NF;a++){printf "%c,\\%03o\n",$a,$a}}' > 3.txt

$ md5sum [123].txt
152fa2b7df00c063702f07aebcb948de  1.txt
152fa2b7df00c063702f07aebcb948de  2.txt
152fa2b7df00c063702f07aebcb948de  3.txt

■awkだとかえって列処理用に文字数が増えるので、シェル用に書き換える。
 8進数の表示も止めて、余分なスペースも改行も無くなった。

$ printf "$(printf '\\%03o' {32..126})";echo
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

■ところで、0..127でなく、32..126にした理由としては、ASCIIコード自体は、0..127まで割り当てられている。

$ echo "$(printf '\\%03o' {0..127})" | sed 's/.\{,60\}/\n&/g'

\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016
\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035
\036\037\040\041\042\043\044\045\046\047\050\051\052\053\054
\055\056\057\060\061\062\063\064\065\066\067\070\071\072\073
\074\075\076\077\100\101\102\103\104\105\106\107\110\111\112
\113\114\115\116\117\120\121\122\123\124\125\126\127\130\131
\132\133\134\135\136\137\140\141\142\143\144\145\146\147\150
\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167
\170\171\172\173\174\175\176\177

■上記をcolのフィルタに通すと、32..126と最後の改行となる。

$ man col | awk '/コントロール文字/&&/エスケープ/'
     col が認識できないコントロール文字およびエスケープシーケンスは削除される。

$ printf "$(printf '\\%03o' {0..127})" | col | od -t d1 -t c
0000000   32   33   34   35   36   37   38   39   40   41   42   43   44   45   46   47
                !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
0000020   48   49   50   51   52   53   54   55   56   57   58   59   60   61   62   63
           0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
0000040   64   65   66   67   68   69   70   71   72   73   74   75   76   77   78   79
           @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O
0000060   80   81   82   83   84   85   86   87   88   89   90   91   92   93   94   95
           P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
0000100   96   97   98   99  100  101  102  103  104  105  106  107  108  109  110  111
           `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
0000120  112  113  114  115  116  117  118  119  120  121  122  123  124  125  126   10
           p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~   \n
0000140

■制御文字やエスケープ文字を表示してみる。
 10番目の「nl」は「\n」

$ printf "$(printf '\\%03o' {0..127})" | od -t d1 -t a
0000000    0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
         nul  soh  stx  etx  eot  enq  ack  bel   bs   ht   nl   vt   ff   cr   so   si
0000020   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31
         dle  dc1  dc2  dc3  dc4  nak  syn  etb  can   em  sub  esc   fs   gs   rs   us
0000040   32   33   34   35   36   37   38   39   40   41   42   43   44   45   46   47
          sp    !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
0000060   48   49   50   51   52   53   54   55   56   57   58   59   60   61   62   63
           0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
0000100   64   65   66   67   68   69   70   71   72   73   74   75   76   77   78   79
           @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O
0000120   80   81   82   83   84   85   86   87   88   89   90   91   92   93   94   95
           P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
0000140   96   97   98   99  100  101  102  103  104  105  106  107  108  109  110  111
           `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
0000160  112  113  114  115  116  117  118  119  120  121  122  123  124  125  126  127
           p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~  del
0000200

■制御文字やエスケープ文字を表示する方法は他にもあって、
 例えばcatにも可視化のオプションがある。
 10文字目で改行されているのは「nl」のため。
 行の終わりには「$」が付く。
 最後は改行で終わっていないので、行の終わりを示す「$」が無い。

$ printf "$(printf '\\%03o' {0..127})" | sed -e 's/[Aa]/\n&/g' | cat -A;echo
^@^A^B^C^D^E^F^G^H^I$
^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_ !"#$%&'()*+,-./0123456789:;<=>?@$
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`$
abcdefghijklmnopqrstuvwxyz{|}~^?

■ROT13
 単換字式暗号。またはシーザー暗号の一つ。
 52文字の半分は26文字は大文字小文字。
 大文字小文字を同一視してその半分は13文字。

$ echo $((52 / 2));echo $((26 / 2))
26
13

$ printf "$(printf '\\%03o\n' {32..126})" | \
    awk '/[A-Za-z]/{print "echo -n \042"$1" \042;echo "$1" | tr \047A-Za-z\047 \047N-ZA-Mn-za-m\047"}' | sh | nl | column -c 40
     1	A N	    27	a n
     2	B O	    28	b o
     3	C P	    29	c p
     4	D Q	    30	d q
     5	E R	    31	e r
     6	F S	    32	f s
     7	G T	    33	g t
     8	H U	    34	h u
     9	I V	    35	i v
    10	J W	    36	j w
    11	K X	    37	k x
    12	L Y	    38	l y
    13	M Z	    39	m z
    14	N A	    40	n a
    15	O B	    41	o b
    16	P C	    42	p c
    17	Q D	    43	q d
    18	R E	    44	r e
    19	S F	    45	s f
    20	T G	    46	t g
    21	U H	    47	u h
    22	V I	    48	v i
    23	W J	    49	w j
    24	X K	    50	x k
    25	Y L	    51	y l
    26	Z M	    52	z m

■ROTATE47は区切り文字のspを除き、94文字を使用する。
 エスケープしないと使えないものばかりなので、
 ほとんど実装されないのも頷ける。

$ echo $((94 / 2))
47

$ printf "$(printf '\\%03o\n' {33..126})" | nl | column -c 120
     1	!	    15	/	    29	=	    43	K	    57	Y	    71	g	    85	u
     2	"	    16	0	    30	>	    44	L	    58	Z	    72	h	    86	v
     3	#	    17	1	    31	?	    45	M	    59	[	    73	i	    87	w
     4	$	    18	2	    32	@	    46	N	    60	\	    74	j	    88	x
     5	%	    19	3	    33	A	    47	O	    61	]	    75	k	    89	y
     6	&	    20	4	    34	B	    48	P	    62	^	    76	l	    90	z
     7	'	    21	5	    35	C	    49	Q	    63	_	    77	m	    91	{
     8	(	    22	6	    36	D	    50	R	    64	`	    78	n	    92	|
     9	)	    23	7	    37	E	    51	S	    65	a	    79	o	    93	}
    10	*	    24	8	    38	F	    52	T	    66	b	    80	p	    94	~
    11	+	    25	9	    39	G	    53	U	    67	c	    81	q
    12	,	    26	:	    40	H	    54	V	    68	d	    82	r
    13	-	    27	;	    41	I	    55	W	    69	e	    83	s
    14	.	    28	<	    42	J	    56	X	    70	f	    84	t

■ところで、大文字小文字を検索する[A-Za-z][A-z]としてはいけない理由もASCIIコードの文字順にある。

$ printf "$(printf '\\%03o\n' {32..126})" | awk '/[A-z]/&&!/[A-Za-z]/' | tr -d '\n' | od -t o1 -t a
0000000 133 134 135 136 137 140
          [   \   ]   ^   _   `
0000006

■SCIIコード表から見て連続していない。

$ printf "$(printf '\\%03o' {65..90})$(printf '\\%03o' {97..122})" | od -t d1 -t a
0000000   65   66   67   68   69   70   71   72   73   74   75   76   77   78   79   80
           A    B    C    D    E    F    G    H    I    J    K    L    M    N    O    P
0000020   81   82   83   84   85   86   87   88   89   90   97   98   99  100  101  102
           Q    R    S    T    U    V    W    X    Y    Z    a    b    c    d    e    f
0000040  103  104  105  106  107  108  109  110  111  112  113  114  115  116  117  118
           g    h    i    j    k    l    m    n    o    p    q    r    s    t    u    v
0000060  119  120  121  122
           w    x    y    z
0000064

■上記を短く書いてもその本質は変わらない。

$ printf "$(printf '\\%03o' {65..90} {97..122})" | od -t d1 -t a
0000000   65   66   67   68   69   70   71   72   73   74   75   76   77   78   79   80
           A    B    C    D    E    F    G    H    I    J    K    L    M    N    O    P
0000020   81   82   83   84   85   86   87   88   89   90   97   98   99  100  101  102
           Q    R    S    T    U    V    W    X    Y    Z    a    b    c    d    e    f
0000040  103  104  105  106  107  108  109  110  111  112  113  114  115  116  117  118
           g    h    i    j    k    l    m    n    o    p    q    r    s    t    u    v
0000060  119  120  121  122
           w    x    y    z
0000064

■[A-z]にしてしまいそうな人のために、awkでは[:alpha:]がある。

$ printf "$(printf '\\%03o\n' {32..126})" | awk '/[[:alpha:]]/' | tr -d '\n' | od -t o1 -t c
0000000 101 102 103 104 105 106 107 110 111 112 113 114 115 116 117 120
          A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P
0000020 121 122 123 124 125 126 127 130 131 132 141 142 143 144 145 146
          Q   R   S   T   U   V   W   X   Y   Z   a   b   c   d   e   f
0000040 147 150 151 152 153 154 155 156 157 160 161 162 163 164 165 166
          g   h   i   j   k   l   m   n   o   p   q   r   s   t   u   v
0000060 167 170 171 172
          w   x   y   z
0000064

■同じ理由で[0-z]と書いても意図した検索は出来ない。
 「{58..64}」の記号が追加で混入してしまう。
 「{91..96}」の記号の出現頻度が低くて見逃されがちだとしても、原因は同じなので注意する。

$ printf "$(printf '\\%03o\n' {32..126})" | awk '/[0-z]/&&!/[[:alnum:]]/' | tr -d '\n' | od -t d1 -t c
0000000   58   59   60   61   62   63   64   91   92   93   94   95   96
           :    ;    <    =    >    ?    @    [    \    ]    ^    _    `
0000015

■逆にsp(スペース)を除きエスケープするべき文字列を、
 awkだけでなくよく使われる8進数エスケープ用に一覧すると、以下のようになる。

$ printf "$(printf '\\%03o\n' {32..126})" | awk '!/[[:alnum:]]| /' | tr -d '\n' | od -t o1 -t c
0000000 041 042 043 044 045 046 047 050 051 052 053 054 055 056 057 072
          !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   :
0000020 073 074 075 076 077 100 133 134 135 136 137 140 173 174 175 176
          ;   <   =   >   ?   @   [   \   ]   ^   _   `   {   |   }   ~
0000040

10進数では{33..47} {58..64} {91..96} {123..126}となる。

$ printf "$(printf '\\%03o' {33..47} {58..64} {91..96} {123..126})"  | od -t d1 -t c
0000000   33   34   35   36   37   38   39   40   41   42   43   44   45   46   47   58
           !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /    :
0000020   59   60   61   62   63   64   91   92   93   94   95   96  123  124  125  126
           ;    <    =    >    ?    @    [    \    ]    ^    _    `    {    |    }    ~
0000040

■一部ならともかく、全部覚えていられないよ。
 ということでググれない環境でエスケープ文字探すときには、
 以下か、その派生シェル芸をすれば良い。

$ printf "$(printf '\\%03o' {0..127})" | col | awk '!/[[:alnum:]]| /' | od -t o1 -t c
0000000 041 042 043 044 045 046 047 050 051 052 053 054 055 056 057 072
          !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   :
0000020 073 074 075 076 077 100 133 134 135 136 137 140 173 174 175 176
          ;   <   =   >   ?   @   [   \   ]   ^   _   `   {   |   }   ~
0000040

■派生シェル芸の例

$ echo {33..126} | awk '{for(a=1;a<=NF;a++)printf "%c\n",$a}' | awk '!/[[:alnum:]]| /' | tr -d '\n' | od -t o1 -t a
0000000 041 042 043 044 045 046 047 050 051 052 053 054 055 056 057 072
          !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   :
0000020 073 074 075 076 077 100 133 134 135 136 137 140 173 174 175 176
          ;   <   =   >   ?   @   [   \   ]   ^   _   `   {   |   }   ~
0000040

$ echo {33..126} | awk '{for(a=1;a<=NF;a++)printf "%c,\\%03o\n",$a,$a}' | awk -F\, '$1 !~ /[[:alnum:]]| /' | column -c 120
!,\041	$,\044	',\047	*,\052	-,\055	:,\072	=,\075	@,\100	],\135	`,\140	},\175
",\042	%,\045	(,\050	+,\053	.,\056	;,\073	>,\076	[,\133	^,\136	{,\173	~,\176
#,\043	&,\046	),\051	,,\054	/,\057	<,\074	?,\077	\,\134	_,\137	|,\174