-
Notifications
You must be signed in to change notification settings - Fork 11
/
ascii.py
executable file
·189 lines (170 loc) · 6.1 KB
/
ascii.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env python3
# Pretty print the ASCII table
#
# 2017, Georg Sauthoff <[email protected]>, GPLv3+
import argparse
import collections
import sys
def mk_arg_parser():
p = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description='Pretty-print ASCII table',
epilog='...')
p.add_argument('--cols', '-c', type=int, default=4,
help='number of columns')
p.add_argument('--explain', '-x', nargs='?', const='all',
help='lookup some abbreviation Example: HT')
return p
def parse_args(*a):
arg_parser = mk_arg_parser()
args = arg_parser.parse_args(*a)
if 128 % args.cols != 0:
raise RuntimeError('#columns not a factor of #chars - try e.g. 4 or 8')
return args
# Source: https://en.wikipedia.org/wiki/ASCII
abbr = {
0 : ( 'NUL' , 'Null' ),
1 : ( 'SOH' , 'Start of Heading' ),
2 : ( 'STX' , 'Start of Text' ),
3 : ( 'ETX' , 'End of Text' ),
4 : ( 'EOT' , 'End of Transmission' ),
5 : ( 'ENQ' , 'Enquiry' ),
6 : ( 'ACK' , 'Acknowledgement' ),
7 : ( 'BEL' , 'Bell' ),
8 : ( 'BS' , 'Backspace' ),
9 : ( 'HT' , 'Horizontal Tab' ),
10 : ( 'LF' , 'Line Feed' ),
11 : ( 'VT' , 'Vertical Tab' ),
12 : ( 'FF' , 'Form Feed' ),
13 : ( 'CR' , 'Carriage Return' ),
14 : ( 'SO' , 'Shift Out' ),
15 : ( 'SI' , 'Shift In' ),
16 : ( 'DLE' , 'Data Link Escape' ),
17 : ( 'DC1' , 'Device Control 1 - often XON' ),
18 : ( 'DC2' , 'Device Control 2' ),
19 : ( 'DC3' , 'Device Control 3 - often XOFF' ),
20 : ( 'DC4' , 'Device Control 4' ),
21 : ( 'NAK' , 'Negative Acknowledgement' ),
22 : ( 'SYN' , 'Synchronous Idle' ),
23 : ( 'ETB' , 'End of Transmission Block' ),
24 : ( 'CAN' , 'Cancel' ),
25 : ( 'EM' , 'End of Medium' ),
26 : ( 'SS' , 'Substitute' ),
27 : ( 'ESC' , 'Escape' ),
28 : ( 'FS' , 'File Separator' ),
29 : ( 'GS' , 'Group Separator' ),
30 : ( 'RS' , 'Record Separator' ),
31 : ( 'US' , 'Unit Separator' ),
32 : ( 'SPC' , 'Space' ),
127 : ( 'DEL' , 'Delete' )
}
desc = collections.OrderedDict((v[0], (v[1], k)) for k, v in abbr.items())
def pp_char(p):
if p < 33 or p == 127:
return abbr[p][0]
else:
return chr(p)
def pp_head(i, w):
return ('{:0' + str(w) + 'b}').format(i)
def pp_row_head(i, w):
return ('{:0' + str(w) + 'b}').format(i)
def pp_table(cols=4):
x = int(128/cols)
rhw = cols.bit_length()-1
chw = 7 - cols.bit_length() + 1
if cols > 1:
for j in range(cols):
print('{:>5}'.format(pp_head(j, rhw)), end='')
print()
for i in range(x):
for j in range(cols):
p = i+j*x
print('{:>5}'.format(pp_char(p)), end='')
print(' {}'.format(pp_row_head(i, chw)))
def explain(s):
if s == 'all':
for k, (v, i) in desc.items():
print('{:>3} = {} ({})'.format(k, v, i))
else:
print('{:>3} = {} ({})'.format(s, *desc[s]))
if __name__ != '__main__':
import io
import unittest.mock as mock
def test_default_table():
with mock.patch('sys.stdout', new=io.StringIO()) as fake_out:
main([])
assert fake_out.getvalue() == ''' 00 01 10 11
NUL SPC @ ` 00000
SOH ! A a 00001
STX " B b 00010
ETX # C c 00011
EOT $ D d 00100
ENQ % E e 00101
ACK & F f 00110
BEL ' G g 00111
BS ( H h 01000
HT ) I i 01001
LF * J j 01010
VT + K k 01011
FF , L l 01100
CR - M m 01101
SO . N n 01110
SI / O o 01111
DLE 0 P p 10000
DC1 1 Q q 10001
DC2 2 R r 10010
DC3 3 S s 10011
DC4 4 T t 10100
NAK 5 U u 10101
SYN 6 V v 10110
ETB 7 W w 10111
CAN 8 X x 11000
EM 9 Y y 11001
SS : Z z 11010
ESC ; [ { 11011
FS < \\ | 11100
GS = ] } 11101
RS > ^ ~ 11110
US ? _ DEL 11111
'''
def test_8col_table():
with mock.patch('sys.stdout', new=io.StringIO()) as fake_out:
main(['-c', '8'])
assert fake_out.getvalue() == ''' 000 001 010 011 100 101 110 111
NUL DLE SPC 0 @ P ` p 0000
SOH DC1 ! 1 A Q a q 0001
STX DC2 " 2 B R b r 0010
ETX DC3 # 3 C S c s 0011
EOT DC4 $ 4 D T d t 0100
ENQ NAK % 5 E U e u 0101
ACK SYN & 6 F V f v 0110
BEL ETB ' 7 G W g w 0111
BS CAN ( 8 H X h x 1000
HT EM ) 9 I Y i y 1001
LF SS * : J Z j z 1010
VT ESC + ; K [ k { 1011
FF FS , < L \\ l | 1100
CR GS - = M ] m } 1101
SO RS . > N ^ n ~ 1110
SI US / ? O _ o DEL 1111
'''
def test_explain():
with mock.patch('sys.stdout', new=io.StringIO()) as fake_out:
main(['-x', 'EOT'])
assert fake_out.getvalue() == 'EOT = End of Transmission (4)\n'
def test_explain_all():
with mock.patch('sys.stdout', new=io.StringIO()) as fake_out:
main(['-x'])
o = fake_out.getvalue()
assert 'EOT = End of Transmission (4)\n' in o
assert ' RS = Record Separator (30)' in o
assert o.splitlines().__len__() == 34
def main(*a):
args = parse_args(*a)
if args.explain:
explain(args.explain)
else:
pp_table(args.cols)
return 0
if __name__ == '__main__':
sys.exit(main())