Search code examples
pythonvigenere

Python Vigenere tuple error


I'm trying to make a Vigenere cipher. When I try to encrypt the message I get the following error.

    cipherCharIndexValue = baseAlphabet.index(keyList[keyIncrement]) + baseAlphabet.index(plainTextChar)
ValueError: tuple.index(x): x not in tuple

I'm not sure what is wrong is causing the error any help?

baseAlphabet = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z')

plainText = input("Please enter the plain text")
key = input("Please enter the key word")
keyList = []
keyLength = 0
while keyLength < len(plainText):
    #Adds the users entered key into a list character by character. 
    #Also makes the key the same length as plainText
    for char in key:
        if keyLength < len(plainText):
            keyList.append(str(char))
            keyLength = keyLength + 1

#The variable each processed letter is appended to
completeCipherText = [] 
#This is the value used to temporaily store the ciphertext character during the iteration
cipherCharIndexValue = 0
keyIncrement = 0

#iterates through the plain text
for plainTextChar in plainText:
        #Adds the base alphabets index value of the key and the plain text char
        cipherCharIndexValue = baseAlphabet.index(keyList[keyIncrement]) + baseAlphabet.index(plainTextChar)
        while cipherCharIndexValue > 25:
             #makes the addition value under 26 as to not go out of range of base alphabet tuple
            cipherCharIndexValue = cipherCharIndexValue - 26 
         #appends the ciphertext character to the completeCipherText variable. 
         #The character is the index of the key + index of the       plainTextChar from baseAlphabet
        completeCipherText.append(baseAlphabet[cipherCharIndexValue])
         #Moves onto the next key
        keyIncrement = keyIncrement + 1
print ('').join(completeCipherText)#Makes the result a strings for printing to the console.

Solution

  • You will get that ValueError: tuple.index(x): x not in tuple error whenever you attempt to get the index of a char that's not in baseAlphabet. So you need to make sure that key only contain such chars, and when encoding plainText either avoid encoding "bad" chars and just copy them to the completeCipherText list unchanged, or convert them to a valid baseAlphabet char.

    In traditional encryption it was common to convert all spaces and punctuation to another character, eg 'x' or '.'. I decided to add '.' to baseAlphabet and write a small function fix_string to perform that operation. fix_string also ensures that all the letters are lower case.

    I've also made a few other minor simplifications to your code.

    There's no need for baseAlphabet to be a tuple. We can use a string. But if you do want to use a tuple of single chars there's no need to write it out in full, you can just pass a string to the tuple constructor, eg

    tuple("some string")
    

    We generally don't need to keep track of the length of collections like lists, strings, etc. All of the built-in collections track their own length and we can efficiently access that length with the len() function.

    We don't need keyIncrement. Instead, we can loop over the chars of keyList and plainText in parallel by using the zip function.

    And instead of using a loop to ensure that the sum of the key and plainText indices are in the proper range we can use the % modulus operator.

    from __future__ import print_function
    
    baseAlphabet = 'abcdefghijklmnopqrstuvwxyz.'
    
    # Process s so that it only contains chars in baseAlphabet
    def fix_string(s):
        # Convert to lower case
        s = s.lower()
        # Convert "other" chars to dot
        a = [ch if ch in baseAlphabet else '.' for ch in s]
        return ''.join(a)
    
    # Vignere cipher
    # mode = 1 to encode, -1 to decode
    def vignere(plainText, key, mode):
        keyList = []
        while len(keyList) < len(plainText):
            # Adds the key into a list character by character. 
            # Also makes the key the same length as plainText
            for char in key:
                if len(keyList) < len(plainText):
                    keyList.append(str(char))
    
        # The variable each processed letter is appended to
        completeCipherText = []
    
        # iterates through the plain text
        for keyChar, plainTextChar in zip(keyList, plainText):
            # Adds the base alphabet's index value of the plain text char and the key char
            cipherCharIndexValue = baseAlphabet.index(plainTextChar) 
            cipherCharIndexValue += mode * baseAlphabet.index(keyChar)
            # makes the addition value in range(len(baseAlphabet))
            cipherCharIndexValue = cipherCharIndexValue % len(baseAlphabet)
    
            # appends the ciphertext character to the completeCipherText variable. 
            # The character is the index of the key + index of the plainTextChar from baseAlphabet
            completeCipherText.append(baseAlphabet[cipherCharIndexValue])
    
        # Makes the result a string
        return ''.join(completeCipherText)
    
    # Test
    
    # plainText = raw_input("Please enter the plain text")
    # key = raw_input("Please enter the key word")
    
    plainText = 'This, is a test string!'
    key = 'the key'
    
    # Process plainText and key so that they only contain chars in baseAlphabet
    plainText = fix_string(plainText)
    key = fix_string(key)
    
    ciphertext = vignere(plainText, key, 1)
    print(ciphertext)
    
    decoded = vignere(ciphertext, key, -1)
    print(decoded)
    

    output

    lomrjdfkgezciplgwsamkzg
    this..is.a.test.string.