A Hexdump Program in Python

Introduction

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:

  • exception handling
  • command-line arguments
  • strings vs. bytes
  • file open modes
  • character encoding and decoding
  • context managers
  • output formatting

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.

Getting Started

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

File Handling

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.

Basic Strategy

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'

Formatting Strategy

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 ints 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..|

A Binary Option

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..|

Finishing Up

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? :)

Current rating: 5

Categories

Tags

Authors

Feeds

RSS / Atom