We're going to roll our own hexdump program here. A hexdump program is a useful tool for a Python programmer. It allows one to learn about UTF-8 and other Unicode character encodings, examine input and output data, troubleshoot Python source code encoding, and so on.
Writing a hexdump program that mimics the operation of the hexdump
program in Linux (also callable as hd
via a soft link) isn't difficult, but it's a great way to learn about a whole bunch of interesting and useful concepts:
As a bonus, it gives developers on Windows a command-line hexdump program that they might not normally have without downloading and installing one (some of the Windows editors/IDEs offer a hex mode).
Here's the file we'll be working with and the hd
output we'll be reproducing:
$ cat testdata
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
$ hd testdata
00000000 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f 6c 6f |Lorem ipsum dolo|
00000010 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f 6e 73 |r sit amet, cons|
00000020 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 |ectetur adipisci|
00000030 6e 67 20 65 6c 69 74 2c 20 73 65 64 20 64 6f 20 |ng elit, sed do |
00000040 65 69 75 73 6d 6f 64 20 74 65 6d 70 6f 72 20 69 |eiusmod tempor i|
00000050 6e 63 69 64 69 64 75 6e 74 20 75 74 20 6c 61 62 |ncididunt ut lab|
00000060 6f 72 65 20 65 74 20 64 6f 6c 6f 72 65 20 6d 61 |ore et dolore ma|
00000070 67 6e 61 20 61 6c 69 71 75 61 2e 0a 55 74 20 65 |gna aliqua..Ut e|
00000080 6e 69 6d 20 61 64 20 6d 69 6e 69 6d 20 76 65 6e |nim ad minim ven|
00000090 69 61 6d 2c 20 71 75 69 73 20 6e 6f 73 74 72 75 |iam, quis nostru|
000000a0 64 20 65 78 65 72 63 69 74 61 74 69 6f 6e 20 75 |d exercitation u|
000000b0 6c 6c 61 6d 63 6f 20 6c 61 62 6f 72 69 73 20 6e |llamco laboris n|
000000c0 69 73 69 20 75 74 20 61 6c 69 71 75 69 70 20 65 |isi ut aliquip e|
000000d0 78 20 65 61 20 63 6f 6d 6d 6f 64 6f 20 63 6f 6e |x ea commodo con|
000000e0 73 65 71 75 61 74 2e 0a 44 75 69 73 20 61 75 74 |sequat..Duis aut|
000000f0 65 20 69 72 75 72 65 20 64 6f 6c 6f 72 20 69 6e |e irure dolor in|
00000100 20 72 65 70 72 65 68 65 6e 64 65 72 69 74 20 69 | reprehenderit i|
00000110 6e 20 76 6f 6c 75 70 74 61 74 65 20 76 65 6c 69 |n voluptate veli|
00000120 74 20 65 73 73 65 20 63 69 6c 6c 75 6d 20 64 6f |t esse cillum do|
00000130 6c 6f 72 65 20 65 75 20 66 75 67 69 61 74 20 6e |lore eu fugiat n|
00000140 75 6c 6c 61 20 70 61 72 69 61 74 75 72 2e 0a 45 |ulla pariatur..E|
00000150 78 63 65 70 74 65 75 72 20 73 69 6e 74 20 6f 63 |xcepteur sint oc|
00000160 63 61 65 63 61 74 20 63 75 70 69 64 61 74 61 74 |caecat cupidatat|
00000170 20 6e 6f 6e 20 70 72 6f 69 64 65 6e 74 2c 20 73 | non proident, s|
00000180 75 6e 74 20 69 6e 20 63 75 6c 70 61 20 71 75 69 |unt in culpa qui|
00000190 20 6f 66 66 69 63 69 61 20 64 65 73 65 72 75 6e | officia deserun|
000001a0 74 20 6d 6f 6c 6c 69 74 20 61 6e 69 6d 20 69 64 |t mollit anim id|
000001b0 20 65 73 74 20 6c 61 62 6f 72 75 6d 2e 0a | est laborum..|
000001be
We'll try to do this in an understandable, educational, and readable way - efficiency is always great but not at the price of obfuscation.
We'll be using Python 3 on Ubuntu.
First of all, we're going to be working with command-line arguments and files. The related program actions can of course lead to "exceptional conditions" (as the professors say) and so we'll use the typical Python EAFP exception handling approach (it's Easier to Ask for Forgiveness than Permission). So we'll start hd.py
with:
import sys
try:
pass
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
We could of course handle exceptions with more granularity but this generic handler will suffice for this example. Note that we're being good programming citizens by printing error messages that contain the name of the program, and printing error messages to sys.stderr
(the reason that we're importing the sys
module).
Regarding command-line arguments, we could of course work directly with the list sys.argv
that always contains the name of the executing Python program as well as the command-line arguments passed to it (i.e. like the array argv
in a C program, sys.argv
always exists and has a length of at least 1 because it always contains the name of the program as specified on the command line in sys.argv[0]
).
Instead we'll use the argparse
module which gives us the ability to support positional (i.e. required) and optional command-line arguments in a simpler and more elegant way. We'll require at least one command-line argument: FILE
that represents the name of the file we wish to dump. We'll create an argument parser object using the constructor ArgumentParser()
and specify the argument we require by calling the add_argument
method on the parser. We'll create the arguments object itself (the command-line arguments are identically-named attributes of this object; this is a Namespace
object in the argparse
module) by calling the parse_args
method on the parser. To test, we'll print out the name of the file using the FILE
attribute of the arguments object:
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("FILE", help="the name of the file that you wish to dump", type=str)
args = parser.parse_args()
try:
print(args.FILE)
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
$ python3 hd.py
usage: hd.py [-h] FILE
hd.py: error: the following arguments are required: FILE
$ python3 hd.py -h
usage: hd.py [-h] FILE
positional arguments:
FILE the name of the file that you wish to dump
optional arguments:
-h, --help show this help message and exit
$ python3 hd.py testdata
testdata
We'll use the typical Python approach to opening files by calling the built-in open
function in a with
statement. The with
statement requires an object that can function as a context manager, i.e. an object of a class that defines __enter__
and __exit__
methods that get called automatically before and after the execution of the code in the with
suite (block). These methods thus effectively function as wrapper methods. Yes, the open
function returns a "file-like object" that we can, for example, call the read
method on, but the class also defines __enter__
and __exit__
methods which means such an object can function as a context manager. This is useful - the __exit__
method conveniently calls the close
method on such an object (per the dispose pattern) even if an exception is raised in the with
suite. This nicely handles what we would otherwise have to handle via a finally
block in a try
statement. Of course, using with
doesn't eliminate the need for exception handling, so we use with
in our try
suite:
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("FILE", help="the name of the file that you wish to dump", type=str)
args = parser.parse_args()
try:
with open(args.FILE, "rb") as f:
pass
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
We discuss the open mode "rb"
next.
Hexdumping a file involves displaying each byte in the file as both a hexadecimal numeric value (interpreting the byte as a binary number) and also (typically) as a character according to a particular character encoding. The Linux hd
uses ASCII encoding, which (as we would say today) is an encoding of the first 128 characters in the Unicode character code in which each character is trivially encoded as the character's code point.
Our strategy will thus be to open the file for reading in binary mode - thus the open mode "rb"
. That means we'll be getting a bytes
object (aka a byte string) each time we read from the file. We don't want to read the file in text mode, getting a str
object of Unicode characters with each read, because a Unicode character can be represented by multiple bytes in the file depending on the file encoding.
We need to display 16 bytes per line so we'll read the data in 16-byte blocks. We'll use Python 3.6's f-strings to simplify formatting. To start with, we'll simply read through the file and display each bytes
object along with it's address in the file (block number * 16):
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("FILE", help="the name of the file that you wish to dump", type=str)
args = parser.parse_args()
try:
with open(args.FILE, "rb") as f:
n = 0 # block number
b = f.read(16) # bytes
while b:
print(f"{n * 16:08x} {b}") # format the address as 8 hex digits with leading zeroes
n += 1
b = f.read(16)
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
$ python3 hd.py testdata
00000000 b'Lorem ipsum dolo'
00000010 b'r sit amet, cons'
00000020 b'ectetur adipisci'
00000030 b'ng elit, sed do '
00000040 b'eiusmod tempor i'
00000050 b'ncididunt ut lab'
00000060 b'ore et dolore ma'
00000070 b'gna aliqua.\nUt e'
00000080 b'nim ad minim ven'
00000090 b'iam, quis nostru'
000000a0 b'd exercitation u'
000000b0 b'llamco laboris n'
000000c0 b'isi ut aliquip e'
000000d0 b'x ea commodo con'
000000e0 b'sequat.\nDuis aut'
000000f0 b'e irure dolor in'
00000100 b' reprehenderit i'
00000110 b'n voluptate veli'
00000120 b't esse cillum do'
00000130 b'lore eu fugiat n'
00000140 b'ulla pariatur.\nE'
00000150 b'xcepteur sint oc'
00000160 b'caecat cupidatat'
00000170 b' non proident, s'
00000180 b'unt in culpa qui'
00000190 b' officia deserun'
000001a0 b't mollit anim id'
000001b0 b' est laborum.\n'
Our strategy for displaying the data in the proper way will be to create two string objects - one for the hex representation and one for the ASCII representation. Any non-printable ASCII character or non-ASCII character (for example, we could have a UTF-8-encoded file) will be displayed as a dot (.). We'll need to somehow iterate over the 16-byte block that we've read, and it's important to note that passing a bytes
object to the list
constructor creates a list of int
objects, not single-byte bytes
objects (in contrast, passing a string to the list
constructor creates a list of single-character string objects). This makes sense because it's the reverse of the more common operation of passing a list of int
s to the bytes
constructor to create a bytes
object. Thus if we iterate using a for
-loop or list comprehension we'll be getting an int
object on each iteration. This behavior is actually useful here because otherwise we would have to create an int
object from each single-byte bytes
object anyway in order to format the data properly.
We'll use string formatting to produce a hex value for each byte and the built-in chr
function to produce an ASCII character for each byte.
We'll iterate over the 16-byte block twice in two list comprehensions and use join
to produce each string. The implicit loop in a comprehension makes this efficient enough compared to using a single normal for
-loop with repeated string concatenation - strings are of course immutable and thus we'd be creating multiple objects in each iteration.
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("FILE", help="the name of the file that you wish to dump", type=str)
args = parser.parse_args()
try:
with open(args.FILE, "rb") as f:
n = 0
b = f.read(16)
while b:
s1 = " ".join([f"{i:02x}" for i in b]) # hex string
s1 = s1[0:23] + " " + s1[23:] # insert extra space between groups of 8 hex values
s2 = "".join([chr(i) if 32 <= i <= 127 else "." for i in b]) # ascii string; chained comparison
print(f"{n * 16:08x} {s1:<48} |{s2}|")
n += 1
b = f.read(16)
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
$ python3 hd.py testdata
00000000 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f 6c 6f |Lorem ipsum dolo|
00000010 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f 6e 73 |r sit amet, cons|
00000020 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 |ectetur adipisci|
00000030 6e 67 20 65 6c 69 74 2c 20 73 65 64 20 64 6f 20 |ng elit, sed do |
00000040 65 69 75 73 6d 6f 64 20 74 65 6d 70 6f 72 20 69 |eiusmod tempor i|
00000050 6e 63 69 64 69 64 75 6e 74 20 75 74 20 6c 61 62 |ncididunt ut lab|
00000060 6f 72 65 20 65 74 20 64 6f 6c 6f 72 65 20 6d 61 |ore et dolore ma|
00000070 67 6e 61 20 61 6c 69 71 75 61 2e 0a 55 74 20 65 |gna aliqua..Ut e|
00000080 6e 69 6d 20 61 64 20 6d 69 6e 69 6d 20 76 65 6e |nim ad minim ven|
00000090 69 61 6d 2c 20 71 75 69 73 20 6e 6f 73 74 72 75 |iam, quis nostru|
000000a0 64 20 65 78 65 72 63 69 74 61 74 69 6f 6e 20 75 |d exercitation u|
000000b0 6c 6c 61 6d 63 6f 20 6c 61 62 6f 72 69 73 20 6e |llamco laboris n|
000000c0 69 73 69 20 75 74 20 61 6c 69 71 75 69 70 20 65 |isi ut aliquip e|
000000d0 78 20 65 61 20 63 6f 6d 6d 6f 64 6f 20 63 6f 6e |x ea commodo con|
000000e0 73 65 71 75 61 74 2e 0a 44 75 69 73 20 61 75 74 |sequat..Duis aut|
000000f0 65 20 69 72 75 72 65 20 64 6f 6c 6f 72 20 69 6e |e irure dolor in|
00000100 20 72 65 70 72 65 68 65 6e 64 65 72 69 74 20 69 | reprehenderit i|
00000110 6e 20 76 6f 6c 75 70 74 61 74 65 20 76 65 6c 69 |n voluptate veli|
00000120 74 20 65 73 73 65 20 63 69 6c 6c 75 6d 20 64 6f |t esse cillum do|
00000130 6c 6f 72 65 20 65 75 20 66 75 67 69 61 74 20 6e |lore eu fugiat n|
00000140 75 6c 6c 61 20 70 61 72 69 61 74 75 72 2e 0a 45 |ulla pariatur..E|
00000150 78 63 65 70 74 65 75 72 20 73 69 6e 74 20 6f 63 |xcepteur sint oc|
00000160 63 61 65 63 61 74 20 63 75 70 69 64 61 74 61 74 |caecat cupidatat|
00000170 20 6e 6f 6e 20 70 72 6f 69 64 65 6e 74 2c 20 73 | non proident, s|
00000180 75 6e 74 20 69 6e 20 63 75 6c 70 61 20 71 75 69 |unt in culpa qui|
00000190 20 6f 66 66 69 63 69 61 20 64 65 73 65 72 75 6e | officia deserun|
000001a0 74 20 6d 6f 6c 6c 69 74 20 61 6e 69 6d 20 69 64 |t mollit anim id|
000001b0 20 65 73 74 20 6c 61 62 6f 72 75 6d 2e 0a | est laborum..|
Note that the program handles files in any encoding e.g. UTF-8 because it works with bytes.
$ cat foo
h€llo wörld
$ python3 hd.py foo
00000000 68 e2 82 ac 6c 6c 6f 20 77 c3 b6 72 6c 64 0a |h...llo w..rld.|
0000000f
Adding an option to show UTF-8 output instead of ASCII output in this case ( |h€__llo wö_rld.|
or something like that) would be an interesting exercise.
Let's add a -b|--binary
option to produce output in binary instead of hex. We'll use a little more flow control for this - the if-else
operator could be used but in this case it would make the code harder to read. We'll parameterize the output field width and show how even format specifiers can be replaced in f-strings. This will also eliminate some buried "magic number" usage (which could be taken further, e.g. using the width in the space insertion operation, BLOCKSIZE = 16, and so on, but we'll leave that as an exercise for the reader :) ):
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("FILE", help="the name of the file that you wish to dump", type=str)
parser.add_argument("-b", "--binary", help="display bytes in binary format instead of hexadecimal", action="store_true")
args = parser.parse_args()
try:
with open(args.FILE, "rb") as f:
n = 0
b = f.read(16)
while b:
if not args.binary:
s1 = " ".join([f"{i:02x}" for i in b])
s1 = s1[0:23] + " " + s1[23:]
width = 48
else:
s1 = " ".join([f"{i:08b}" for i in b])
s1 = s1[0:71] + " " + s1[71:]
width = 144
s2 = "".join([chr(i) if 32 <= i <= 127 else "." for i in b])
print(f"{n * 16:08x} {s1:<{width}} |{s2}|") # parameterized width
n += 1
b = f.read(16)
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
$ python3 hd.py
usage: hd.py [-h] [-b] FILE
hd.py: error: the following arguments are required: FILE
$ python3 hd.py -h
usage: hd.py [-h] [-b] FILE
positional arguments:
FILE the name of the file that you wish to dump
optional arguments:
-h, --help show this help message and exit
-b, --binary display bytes in binary format instead of hexadecimal
$ python3 hd.py -b testdata
00000000 01001100 01101111 01110010 01100101 01101101 00100000 01101001 01110000 01110011 01110101 01101101 00100000 01100100 01101111 01101100 01101111 |Lorem ipsum dolo|
00000010 01110010 00100000 01110011 01101001 01110100 00100000 01100001 01101101 01100101 01110100 00101100 00100000 01100011 01101111 01101110 01110011 |r sit amet, cons|
00000020 01100101 01100011 01110100 01100101 01110100 01110101 01110010 00100000 01100001 01100100 01101001 01110000 01101001 01110011 01100011 01101001 |ectetur adipisci|
00000030 01101110 01100111 00100000 01100101 01101100 01101001 01110100 00101100 00100000 01110011 01100101 01100100 00100000 01100100 01101111 00100000 |ng elit, sed do |
00000040 01100101 01101001 01110101 01110011 01101101 01101111 01100100 00100000 01110100 01100101 01101101 01110000 01101111 01110010 00100000 01101001 |eiusmod tempor i|
00000050 01101110 01100011 01101001 01100100 01101001 01100100 01110101 01101110 01110100 00100000 01110101 01110100 00100000 01101100 01100001 01100010 |ncididunt ut lab|
00000060 01101111 01110010 01100101 00100000 01100101 01110100 00100000 01100100 01101111 01101100 01101111 01110010 01100101 00100000 01101101 01100001 |ore et dolore ma|
00000070 01100111 01101110 01100001 00100000 01100001 01101100 01101001 01110001 01110101 01100001 00101110 00001010 01010101 01110100 00100000 01100101 |gna aliqua..Ut e|
00000080 01101110 01101001 01101101 00100000 01100001 01100100 00100000 01101101 01101001 01101110 01101001 01101101 00100000 01110110 01100101 01101110 |nim ad minim ven|
00000090 01101001 01100001 01101101 00101100 00100000 01110001 01110101 01101001 01110011 00100000 01101110 01101111 01110011 01110100 01110010 01110101 |iam, quis nostru|
000000a0 01100100 00100000 01100101 01111000 01100101 01110010 01100011 01101001 01110100 01100001 01110100 01101001 01101111 01101110 00100000 01110101 |d exercitation u|
000000b0 01101100 01101100 01100001 01101101 01100011 01101111 00100000 01101100 01100001 01100010 01101111 01110010 01101001 01110011 00100000 01101110 |llamco laboris n|
000000c0 01101001 01110011 01101001 00100000 01110101 01110100 00100000 01100001 01101100 01101001 01110001 01110101 01101001 01110000 00100000 01100101 |isi ut aliquip e|
000000d0 01111000 00100000 01100101 01100001 00100000 01100011 01101111 01101101 01101101 01101111 01100100 01101111 00100000 01100011 01101111 01101110 |x ea commodo con|
000000e0 01110011 01100101 01110001 01110101 01100001 01110100 00101110 00001010 01000100 01110101 01101001 01110011 00100000 01100001 01110101 01110100 |sequat..Duis aut|
000000f0 01100101 00100000 01101001 01110010 01110101 01110010 01100101 00100000 01100100 01101111 01101100 01101111 01110010 00100000 01101001 01101110 |e irure dolor in|
00000100 00100000 01110010 01100101 01110000 01110010 01100101 01101000 01100101 01101110 01100100 01100101 01110010 01101001 01110100 00100000 01101001 | reprehenderit i|
00000110 01101110 00100000 01110110 01101111 01101100 01110101 01110000 01110100 01100001 01110100 01100101 00100000 01110110 01100101 01101100 01101001 |n voluptate veli|
00000120 01110100 00100000 01100101 01110011 01110011 01100101 00100000 01100011 01101001 01101100 01101100 01110101 01101101 00100000 01100100 01101111 |t esse cillum do|
00000130 01101100 01101111 01110010 01100101 00100000 01100101 01110101 00100000 01100110 01110101 01100111 01101001 01100001 01110100 00100000 01101110 |lore eu fugiat n|
00000140 01110101 01101100 01101100 01100001 00100000 01110000 01100001 01110010 01101001 01100001 01110100 01110101 01110010 00101110 00001010 01000101 |ulla pariatur..E|
00000150 01111000 01100011 01100101 01110000 01110100 01100101 01110101 01110010 00100000 01110011 01101001 01101110 01110100 00100000 01101111 01100011 |xcepteur sint oc|
00000160 01100011 01100001 01100101 01100011 01100001 01110100 00100000 01100011 01110101 01110000 01101001 01100100 01100001 01110100 01100001 01110100 |caecat cupidatat|
00000170 00100000 01101110 01101111 01101110 00100000 01110000 01110010 01101111 01101001 01100100 01100101 01101110 01110100 00101100 00100000 01110011 | non proident, s|
00000180 01110101 01101110 01110100 00100000 01101001 01101110 00100000 01100011 01110101 01101100 01110000 01100001 00100000 01110001 01110101 01101001 |unt in culpa qui|
00000190 00100000 01101111 01100110 01100110 01101001 01100011 01101001 01100001 00100000 01100100 01100101 01110011 01100101 01110010 01110101 01101110 | officia deserun|
000001a0 01110100 00100000 01101101 01101111 01101100 01101100 01101001 01110100 00100000 01100001 01101110 01101001 01101101 00100000 01101001 01100100 |t mollit anim id|
000001b0 00100000 01100101 01110011 01110100 00100000 01101100 01100001 01100010 01101111 01110010 01110101 01101101 00101110 00001010 | est laborum..|
The Linux hd
displays the total number of bytes in the file on the last line (1beh or 446d in our example). We could determine this via accumulation of course, but instead we'll import os.path
and use getsize
.
import sys
import argparse
import os.path
parser = argparse.ArgumentParser()
parser.add_argument("FILE", help="the name of the file that you wish to dump", type=str)
parser.add_argument("-b", "--binary", help="display bytes in binary format instead of hexadecimal", action="store_true")
args = parser.parse_args()
try:
with open(args.FILE, "rb") as f:
n = 0
b = f.read(16)
while b:
if not args.binary:
s1 = " ".join([f"{i:02x}" for i in b])
s1 = s1[0:23] + " " + s1[23:]
width = 48
else:
s1 = " ".join([f"{i:08b}" for i in b])
s1 = s1[0:71] + " " + s1[71:]
width = 144
s2 = "".join([chr(i) if 32 <= i <= 127 else "." for i in b])
print(f"{n * 16:08x} {s1:<{width}} |{s2}|") # parameterized width
n += 1
b = f.read(16)
print(f"{os.path.getsize(args.FILE):08x}")
except Exception as e:
print(__file__, ": ", type(e).__name__, " - ", e, sep="", file=sys.stderr)
$ python3 hd.py testdata
00000000 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f 6c 6f |Lorem ipsum dolo|
00000010 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f 6e 73 |r sit amet, cons|
00000020 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 |ectetur adipisci|
00000030 6e 67 20 65 6c 69 74 2c 20 73 65 64 20 64 6f 20 |ng elit, sed do |
00000040 65 69 75 73 6d 6f 64 20 74 65 6d 70 6f 72 20 69 |eiusmod tempor i|
00000050 6e 63 69 64 69 64 75 6e 74 20 75 74 20 6c 61 62 |ncididunt ut lab|
00000060 6f 72 65 20 65 74 20 64 6f 6c 6f 72 65 20 6d 61 |ore et dolore ma|
00000070 67 6e 61 20 61 6c 69 71 75 61 2e 0a 55 74 20 65 |gna aliqua..Ut e|
00000080 6e 69 6d 20 61 64 20 6d 69 6e 69 6d 20 76 65 6e |nim ad minim ven|
00000090 69 61 6d 2c 20 71 75 69 73 20 6e 6f 73 74 72 75 |iam, quis nostru|
000000a0 64 20 65 78 65 72 63 69 74 61 74 69 6f 6e 20 75 |d exercitation u|
000000b0 6c 6c 61 6d 63 6f 20 6c 61 62 6f 72 69 73 20 6e |llamco laboris n|
000000c0 69 73 69 20 75 74 20 61 6c 69 71 75 69 70 20 65 |isi ut aliquip e|
000000d0 78 20 65 61 20 63 6f 6d 6d 6f 64 6f 20 63 6f 6e |x ea commodo con|
000000e0 73 65 71 75 61 74 2e 0a 44 75 69 73 20 61 75 74 |sequat..Duis aut|
000000f0 65 20 69 72 75 72 65 20 64 6f 6c 6f 72 20 69 6e |e irure dolor in|
00000100 20 72 65 70 72 65 68 65 6e 64 65 72 69 74 20 69 | reprehenderit i|
00000110 6e 20 76 6f 6c 75 70 74 61 74 65 20 76 65 6c 69 |n voluptate veli|
00000120 74 20 65 73 73 65 20 63 69 6c 6c 75 6d 20 64 6f |t esse cillum do|
00000130 6c 6f 72 65 20 65 75 20 66 75 67 69 61 74 20 6e |lore eu fugiat n|
00000140 75 6c 6c 61 20 70 61 72 69 61 74 75 72 2e 0a 45 |ulla pariatur..E|
00000150 78 63 65 70 74 65 75 72 20 73 69 6e 74 20 6f 63 |xcepteur sint oc|
00000160 63 61 65 63 61 74 20 63 75 70 69 64 61 74 61 74 |caecat cupidatat|
00000170 20 6e 6f 6e 20 70 72 6f 69 64 65 6e 74 2c 20 73 | non proident, s|
00000180 75 6e 74 20 69 6e 20 63 75 6c 70 61 20 71 75 69 |unt in culpa qui|
00000190 20 6f 66 66 69 63 69 61 20 64 65 73 65 72 75 6e | officia deserun|
000001a0 74 20 6d 6f 6c 6c 69 74 20 61 6e 69 6d 20 69 64 |t mollit anim id|
000001b0 20 65 73 74 20 6c 61 62 6f 72 75 6d 2e 0a | est laborum..|
000001be
And there we are. hd
in Python. Who needs a C compiler anyway? :)