|
| 1 | +from __future__ import print_function |
| 2 | +import os |
| 3 | +import string |
| 4 | +import argparse |
| 5 | + |
| 6 | +try: |
| 7 | + maketrans = string.maketrans # python2 |
| 8 | +except AttributeError: |
| 9 | + maketrans = str.maketrans # python3 |
| 10 | + |
| 11 | + |
| 12 | +def caeser_cipher(string_: str, offset: int, decode: bool, file_: string) -> None: |
| 13 | + """ Caeser Cipher implementation, reads file or string. Also decodes. |
| 14 | +
|
| 15 | + Default implementation is ROT13 encoding. |
| 16 | +
|
| 17 | + To decode, specify the same offset you used to encode and your ciphertext / file. |
| 18 | +
|
| 19 | + :param string_: string to encode / decode |
| 20 | + :param offset: # of chars to rotate by |
| 21 | + :param decode: decode instead of encode |
| 22 | + :param file_: file to read in then encode/decode |
| 23 | + """ |
| 24 | + if file_ and os.path.exists(file_): |
| 25 | + with open(file_, 'r') as f: |
| 26 | + string_ = f.read() |
| 27 | + |
| 28 | + if decode: |
| 29 | + offset *= -1 |
| 30 | + |
| 31 | + lower_offset_alphabet = string.ascii_lowercase[offset:] + string.ascii_lowercase[:offset] |
| 32 | + lower_translation_table = maketrans(string.ascii_lowercase, lower_offset_alphabet) |
| 33 | + |
| 34 | + upper_offset_alphabet = string.ascii_uppercase[offset:] + string.ascii_uppercase[:offset] |
| 35 | + upper_translation_table = maketrans(string.ascii_uppercase, upper_offset_alphabet) |
| 36 | + |
| 37 | + lower_converted = string_.translate(lower_translation_table) |
| 38 | + final_converted = lower_converted.translate(upper_translation_table) |
| 39 | + |
| 40 | + if file_: |
| 41 | + extension = 'dec' if decode else 'enc' |
| 42 | + with open("{}.{}".format(file_, extension), 'w') as f: |
| 43 | + print(final_converted, file=f) |
| 44 | + else: |
| 45 | + print(final_converted) |
| 46 | + |
| 47 | + |
| 48 | +def check_offset_range(value: int) -> int: |
| 49 | + """ Validates that value is in the allowable range. |
| 50 | +
|
| 51 | + :param value: integer to validate |
| 52 | + :return: valid integer |
| 53 | + :raises: argparse.ArgumentTypeError |
| 54 | + """ |
| 55 | + value = int(value) |
| 56 | + if value < -25 or value > 25: |
| 57 | + raise argparse.ArgumentTypeError("{} is an invalid offset".format(value)) |
| 58 | + return value |
| 59 | + |
| 60 | + |
| 61 | +if __name__ == '__main__': |
| 62 | + parser = argparse.ArgumentParser(description="Simple Caeser Cipher [En,De]coder") |
| 63 | + |
| 64 | + parser.add_argument('-d', '--decode', action='store_true', dest='decode', |
| 65 | + help='decode ciphertext (offset should equal what was used to encode)', default=False) |
| 66 | + parser.add_argument('-o', '--offset', dest='offset', default=13, type=check_offset_range, |
| 67 | + help='number of characters to shift') |
| 68 | + |
| 69 | + group = parser.add_mutually_exclusive_group(required=True) |
| 70 | + group.add_argument('-f', '--file', dest='file', help='file to encode', default=None) |
| 71 | + group.add_argument('-s', '--string', dest='string', help='string to encode', default=None) |
| 72 | + |
| 73 | + args = parser.parse_args() |
| 74 | + |
| 75 | + caeser_cipher(args.string, args.offset, args.decode, args.file) |
0 commit comments