Search code examples
pythondictionaryletter

How to change random letter using number in Python


I am working on a program in Python that sort-of encrypts a message. I have converted the string (which will vary based on user input) to a list and have a number. I want to automatically change each letter in the list with the number. For example, if the number is three, every A changes into a D. I have a dictionary with the vales for letters, such as {"a" : 1}, {"b" : 2}, etc.

I can not seem to get how I could change the letters (without knowing what they are) and possibly in accordance with my dictionary.

What I have so far (dictionary is somewhere else):

def numtolist(n):
    seedstring = str(n)
    numlist = []
    for digit in seedstring:
        numlist.append(int(digit))
    return numlist

currentnumber = seed^2
newmessage = str()
for letter in messageList:
    numtolist(currentnumber)
    num1 = numlist[0]

Solution

  • If the transform is as simple as an alphabetic shift you can simply do it by:

    if original_character.isupper():  # Determine whether it is upper/lower case
        base_a = 'A'                  # use 'A' as a base for uppercase
    else:                             #  
        base_a = 'a'                  # use 'a' as a base for lowercase
    
    
    orig_char_ascii = ord(original_character)  # Get the original ascii value
    
    orig_char_alpha_index = orig_char_ascii - ord(base_a) # Get the `0-25` alphabetic
                                                          # index of the character
    
    shift_number = 3  # Set the amount to shift by
    
    new_char_alpha_index = orig_char_alpha_index + shift_number # Add the shift value
    
    new_char_alpha_index = new_char_alpha_index % 26  # Take the modulus to impose
                                                      # periodic boundary conditions
                                                      # i.e. if you had 'z' + 3
                                                      #      'z' + 3 -> 25 + 3 -> 28
                                                      #      28 % 26 = 2,  2 -> 'c'
    
    new_char_ascii_index = ord(base_a) + new_char_alpha_index  # scale back to the ascii
                                                               # value
    
    new_char = chr(new_char_ascii_index)
    

    The basic idea is that each character corresponds to an ascii number which can be gotten by ord (i.e. ord('a') = 97). The chr method reverses this: chr(97) = 'a'.

    The method is briefly: you get the ascii value, scale to a 0-25 alphabetic range, add your shift, wrap values that overflow the alphabetic range, scale back to ascii, and get a character back via chr.

    You can compact this method a lot, I was verbose in the name of education:

    def shift_char(ch,shift):
        if not ch.isalpha():
            return ch   # return non-alphabetic characters as is
        is_upper = 'A' <= original_character <= 'Z'
        if is_upper:
            base_a = 'A'
        else:
            base_a = 'a'
        return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
    

    If you wanted to use the same random shift for a whole string you could split this into a couple methods:

    def shift_char(ch,shift):
        if not ch.isalpha():
            return ch   # return non-alphabetic characters as is
        if ch.isupper():
            base_a = 'A'
        else:
            base_a = 'a'
        return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
    
    def shift_string(s,shift):
        return ''.join(shift_char(i,r) for i in s)
    

    If you want to be able to decode it later you need a reversal method and to store the shift value:

    def shift_char(ch,shift):
        if not ch.isalpha():
            return ch   # return non-alphabetic characters as is
        if ch.isupper():
            base_a = 'A'
        else:
            base_a = 'a'
        return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
    
    def encode_string(s,shift):
        return ''.join(shift_char(i,r) for i in s), shift  # NOTE EXTRA return value
    
    def decode_string(s,shift):
        return ''.join(shift_char(i,-shift) for i in s)
    
    # usage
    s = 'Hello, StackOverflow!'
    enc_s,shift = encode_string(s)
    dec_s = decode_string(enc_s,shift)
    

    If your shift is random you can just pass that as the argument:

    import random
    
    def shift_char(ch,shift):
        if not ch.isalpha():
            return ch   # return non-alphabetic characters as is
        if ch.isupper():
            base_a = 'A'
        else:
            base_a = 'a'
        return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
    
    def encode_string(s,shift):
        return ''.join(shift_char(i,shift) for i in s)
    
    def decode_string(s,shift):
        return ''.join(shift_char(i,-shift) for i in s)
    
    # usage
    s = 'Hello, StackOverflow!'
    shift = random.randint(1,25)
    enc_s = encode_string(s,shift)
    dec_s = decode_string(enc_s,shift)
    

    or rearrange the methods

    import random
    
    def shift_char(ch,shift):
        if not ch.isalpha():
            return ch   # return non-alphabetic characters as is
        if ch.isupper():
            base_a = 'A'
        else:
            base_a = 'a'
        return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
    
    def encode_string(s):
        shift = random.randint(1,25)
        return ''.join(shift_char(i,shift) for i in s), shift
    
    def decode_string(s,shift):
        return ''.join(shift_char(i,-shift) for i in s)
    
    # usage
    s = 'Hello, StackOverflow!'
    enc_s, shift = encode_string(s)
    dec_s = decode_string(enc_s,shift)