# Contest Solution: Packet Sniffer

The Problem

Sniffed any good packets lately, dude? Hopefully you haven’t done this illegally, but rest assured that there are some who have. If you surf the Internet, you have probably visited companies’ net sites where they sell their products online: you give them your credit card number and they ship you the goods. That’s a convenient way to shop if you can ensure that your credit card number isn’t being “sniffed” up by wily hackers and used illicitly.

The Internet is an example of a packet-switched network. This means that information is sent in discrete groups of bits called packets from computer to computer. For example, when I send email to someone in the Philippines, my message (at the binary level) is broken up into packets, and routed packet by packet (not all at once) from computer to computer, ultimately to the recipient’s computer.

“Packet sniffing” refers to writing a program that grabs the individual packets that come your computer’s way and reads their contents. Now the term “packet sniffing” has obvious unethical connotations: usually it refers to writing a program that reads packets addressed to computers other than your own. But the principle is exactly the same when you only intercept those packets that are intended for you.

Let’s suppose that a network packet consists of three parts: a special start-of-packet bit pattern,the packet content, and a special end-of-packet bit pattern. Suppose that the start- and end-of-packet pattern are both 1000001 and that the packet content is no more than three consecutive sequences of 7 bits(1’s and 0’s). So, to write a program to “sniff” packets you need only to write a program that scans its input and “decodes” everything between pairs of 1000001.

For this problem assume that the content of each packet is binary representation of no more than three ASCII characters, each encoded in 7 “bits” (0’s and 1’s). Your task is to write a program that prints out the message (in English) that is being transmitted by a sequence of packets.

Sample Input

Take your input from the text file prob4.in. Here’s a sample of its content:

10000011100010110111111011111000001100000101000011000001

You are guaranteed two things: (1) the encoding is correct, and (2) only lower case letters and punctuation are encoded in the packet.

Sample Output

Your program should direct its output to the screen. The correct output for the sample input above is:

boo!

packetsniffer.py

with open('prob4.in') as f:
for line in f:
line = line.strip().replace('1000001', '')
print(''.join([chr(int(c, 2)) for c in [line[i:i+7] for i in range(0, len(line), 7)]]))

Any advice on performance enhancement, solution simplification or that is topical is appreciated!

• So it cannot happen that '1000001' is generated by two consective asci characters? Nov 1, 2018 at 14:42
• @miracle173 My guess would be yes, since that violates some of the rules established by the problem. Though I wouldn't mind seeing that case handled ;)
– T145
Nov 1, 2018 at 14:48
• which rule does this violate? Nov 1, 2018 at 15:09
• An empty packet would be kind of pointless. It's implied that we're going to be getting something btwn. the delimiting phrases. Or maybe I'm misunderstanding your question.
– T145
Nov 1, 2018 at 17:31
• a package with an empty content does not violate the rules. The rule says "not more than three". But that was not my objection. I asked, what if something like this happens "...1000 0001..", a char ending with 1000 is followed by a char starting with 0001? You say there is a packacge delimiter 10000001 and remove it, this is wrong. Nov 1, 2018 at 18:34

Your code looks good, except that you do not really need the c, and hence the nesting, in the list comprehension. You can use line[i:i+7] directly in int, making it a bit shorter:

''.join([chr(int(line[i:i+7], 2)) for i in range(0, len(line), 7)])

However, just removing the 1000001 in the line before seems a bit arbitrary, since (a) the pattern might also be present within a package, spanning two bytes. Instead, you could keep the c variable and ignore the current c if it equals the package delimiter:

>>> line = "10000011100010110111111011111000001100000101000011000001"
>>> ''.join([chr(int(c, 2)) for c in (line[i:i+7] for i in range(0, len(line), 7)) if c != "1000001"])
'boo!'

But, you still lose the information where the packages start and end. Instead, you could use regular expressions for first detecting the packages, and then finding the bytes within those:

>>> p = r"1000001((?:[01]{7}){,3})1000001"
>>> re.findall(p, line)
['110001011011111101111', '0100001']

Here, p means "package delimiter, followed by up to three groups of seven 0 or 1, followed by delimiter. Then you can use another regex q to split those into bytes.

>>> q = r"[01]{7}"
>>> [''.join(chr(int(x, 2)) for x in re.findall(q, y)) for y in re.findall(p, line)]
['boo', '!']

Of course, the regex q is a bit redundant, since we already checked the same in p. You could also do this part using a range and string slicing, but personally I still find the regex approach cleaner.

>>> [''.join(chr(int(y[i:i+7], 2)) for i in range(0, len(y), 7)) for y in r e.findall(p, bits)]
['boo', '!']

Another way, without regex, might be to first separate the line into groups of 7 bits, and then using itertools.groupby to group those parts into package-delimiter and package-contents:

>>> parts = [line[i:i+7] for i in range(0, len(line), 7)]
>>> groups = [list(g) for k, g in groupby(parts, key="1000001".__ne__) if k]
>>> [''.join(chr(int(b, 2)) for b in g) for g in groups]
['boo', '!']