I have created an hex viewer in python, as a timed-challenge by friend. My implementation was in the form of a class, with a separate main file running with argparse
to allow choosing the file (runs with a demo file by default).
I was pretty satisfied with the final result. However, I have used many list comprehensions and mapping to cut up times. How can I improve the code or considerate styling standards? Any other advice regarding the code or the functionality?
The code is divided into 3 files, first one is general utils for the task, second is the main class and the third is the runner:
gen.py
import string
def hexa (num, fill = 2):
return hex(num)[2:].lower().zfill(fill)
def bina (num, fill = 8):
return bin(num)[2:].zfill(fill)
def chunks (arr, size = 1):
return [arr[i: i+size] for i in range(0, len(arr), size)]
def lmap (func, iterable):
return list(map(func, iterable))
hex_digits_chunks = chunks(lmap(hexa, range(16)), 4)
printable_ascii = lmap(ord, string.digits + string.ascii_letters + string.punctuation)
hexview.py
from gen import *
class HexViewer ():
def __init__ (self, file):
self.data = open(file, 'rb').read()
self.hex_data = lmap(hexa, self.data)
self.hex_chunks = chunks(chunks(self.hex_data, 4), 4)
self.ascii_data = [(chr(int(byte, 16)) if int(byte, 16) in printable_ascii else '.') for byte in self.hex_data]
self.ascii_chunks = chunks(self.ascii_data, 16)
self.rows = len(self.hex_chunks)
self.addresses = lmap(lambda o: hexa(o * 16, 8), range(0, self.rows))
def __str__ (self):
table_format = ' {:<15}{:<60}{:<20}\n'
str_rep = ''
str_rep += table_format.format(
'address'.upper(),
' '.join(' '.join(x) for x in hex_digits_chunks),
'ascii'.upper())
str_rep += '\n'
for i in range(self.rows):
str_rep += table_format.format(
self.addresses[i],
' '.join(' '.join(x) for x in self.hex_chunks[i]),
''.join(self.ascii_chunks[i]))
return str_rep
main.py
import traceback
import argparse
from gen import *
from hexview import *
try:
parser = argparse.ArgumentParser(description='Hexadeciaml viewer.')
parser.add_argument('file', type=str, nargs='?', default='demo.exe', help='the file to process')
args = parser.parse_args()
print('\n\n')
print(HexViewer(args.file))
except SystemExit:
pass
except:
traceback.print_exc()
Demo:
C:\...\Hexed> main.py
ADDRESS 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ASCII
00000000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 MZ..............
00000010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ................
00000040 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 ........!..L.!Th
00000050 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f is.program.canno
00000060 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 t.be.run.in.DOS.
00000070 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 mode....$.......
00000080 50 45 00 00 4c 01 02 00 00 00 00 00 00 00 00 00 PE..L...........
... and you got the idea ...