Hey, it's me again. I have a Ceaser Cypher here:

letters=list("abcdefghijklmnopqrstuvwxyz")

def Encrypt():
    for i in range(int((len(word)-1))):
        curletter="".join(letters).find(word[i])
        if (curletter+key)>(25):
            if (curletter+key)<0:
                i=(curletter+key)
                i=[(curletter+key)-(curletter+key)]
            else:
                i=(curletter+key)-(25)
        print (curletter+key)
        word[i]=letters[curletter+key]
    return

def Decrypt():
    return
    
word=list(input("Type in some letters:"))


key=int(input("Type in a key:"))

choice=int(input("1) Encrypt\n2) Decrypt\n:"))
if choice==1:
    Encrypt()
else:
    Decrypt()


print("".join(word))

If you run it and type in something that is 26 or more letters (no spaces) and you use a key of 1, it runs, and you get an index error. However, when I used print to see why letters[currentletter+key] was out of range, the number preceding it was 26, when the list only goes to 25. I see no reason why this code:

if (curletter+key)>(25):
    if (curletter+key)<0:
        i=(curletter+key)
        i=[(curletter+key)-(curletter+key)]
     else:
        i=(curletter+key)-(25)

Doesn't filter out numbers larger then 25. i is the "for" variable, currentletter is the list number of the letter you're on (a=0, b=1, c=2 etc). Any help from some python coding gurus would be much appreciated.

Dani AI

Generated

The IndexError is coming from using the un-wrapped value curletter + key to index letters. When that sum becomes 26 you try to read letters[26] (valid indices are 0..25). Two specific bugs to fix right away:

  • The nested test if (curletter+key) > 25: if (curletter+key) < 0: is impossible (a number cannot be >25 and <0 at the same time). Also subtracting 25 is an off-by-one mistake — if you adjust manually you must subtract 26, but the simpler, safer way is to wrap with modulo.
  • Don’t reassign the loop variable i inside the loop, and don’t iterate range(len(word)-1) (that skips the last character). Using find on "".join(letters) every iteration is also fragile: it will return -1 for characters not in letters (uppercase, punctuation), which then makes the index arithmetic worse.

A robust approach: reduce the key with key %= 26, compute the wrapped index once, and build a new output string rather than mutating the input list while iterating. Using ord/chr is both compact and fast and lets you preserve case and non-alpha characters.

Example (Python 3) implementation:

def caesar(text, key):
    key %= 26
    out = []
    for ch in text:
        if 'a' <= ch <= 'z':
            out.append(chr((ord(ch) - ord('a') + key) % 26 + ord('a')))
        elif 'A' <= ch <= 'Z':
            out.append(chr((ord(ch) - ord('A') + key) % 26 + ord('A')))
        else:
            out.append(ch)
    return ''.join(out)

This follows ’s suggestion to use ord/chr and the modulo idea mentioned by , while avoiding in-place index reassignment and the range/offset mistakes. Quick tests: "a" +1 -> "b", "z" +1 -> "a", and large keys work because of key %= 26.

Recommended Answers

All 8 Replies

This will never be true

if (curletter+key)>(25):
            if (curletter+key)<0:

Additionally what happens when user inputs key 123456? Why you use global variables, not parameters. Also would be natural to return values.

if (curletter+key)>(25):
            if (curletter+key)<0:

I don't know why I didn't see that.
Even after removing it however, 26 still slips through.

Additionally what happens when user inputs key 123456?

I never realized that, but all you have to do is divide it, because any key past 26 can be reduced.

Why you use global variables, not parameters. Also would be natural to return values

As far as global variables, are you implying I just need to take out the return? Or should I put all the variables in the methods parameters and skip the return command.

Either way, the program still isn't working and 26 still isn't getting filtered out.

check % operator and ord and chr functions.

I can use % to check for remainders and stuff, thanks.However, I get how ord and chr work, but wouldn't it simply be a more less, built in list? If it's a substitute for the out of range problem, it still wouldn't work because I would have some random character in the cipher.

If you use modulo you do not go out of range.

And this will always be zero.

i=[(curletter+key)-(curletter+key)]

You can just add or subtract the shift value, depending on whether it is encrypt or decrypt, and test for < 0 or > length and add or subtract length. You can in fact use the same function for encryption and decryption by sending a switch to the function telling it which you want to do.

def do_some_testing(letter_in):
    ## decrypt = shift each letter backward by 3 letters
    letters=list("abcdefghijklmnopqrstuvwxyz")
    shift = 3 

    if letter_in in letters:
        current_location = letters.index(letter_in)
        new_location = current_location - shift
        if new_location < 0:
            new_location += len(letters)
        return(letters[new_location])
    return("*")                         ## error

##------------------------------------------------
print "z -->",
print do_some_testing("z")

print "a -->",
print do_some_testing("a")

print "-"*30
orig_string = "d txlfn eurzq ira"
decrypt_list = []
for letter in orig_string:
    decrypt_list.append(do_some_testing(letter))  
print orig_string
print "".join(decrypt_list), "\n"

Thanks woooe. I'll implement that code into my version.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.