I'm trying to use a 2d grid cipher to encrypt a string in Python, but my nested loops are causing out of range errors.
Here's my code:
def encrypt():
before = str(input("Type a string to encrypt: "))
columns = int(input("How many table columns would you like: "))
split = [before[i:i+columns] for i in range(0, len(before), columns)]
rows = len(split)
after = []
for i in range(0, columns):
for j in range(0,rows):
after.append(split[j][i])
print(after)
And here's the error I am receiving:
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
encrypt()
File "E:/Python/cipher.py", line 12, in encrypt
after.append(split[j][i])
IndexError: string index out of range
3 Answers 3
The problem occurs because your input string is not guaranteed to be a multiple of the rows. For example, using 3 columns "Input String to Encrypt" fails to encrypt because it produces a split list of ['Inp', 'ut ', 'Str', 'ing', ' to', ' En', 'cry', 'pt']. Notice the last element in the array only has 2 elements.
If you pad your input string with a space like: "Input String to Encrypt " The encryption works as the split produces: ['Inp', 'ut ', 'Str', 'ing', ' to', ' En', 'cry', 'pt ']
1 Comment
The problem (try printing split) is that all elements in split are not necessarily rows long:
Type a string to encrypt: hello world
How many table columns would you like: 3
['hel', 'lo ', 'wor', 'ld']
You have to decide what you want to do. Maybe append spaces to the end of the last string if it's not long enough.
You might also want to have a look at enumerate. Happy hacking.
Update: Let's say you choose to use 2 columns, and a string length that is divisible by two:
Type a string to encrypt: helo
How many table columns would you like: 2
['he', 'lo']
['h', 'l', 'e', 'o']
Seems to work. Oh, I had to change your code a little because input doesn't do what you think:
def encrypt():
before = raw_input("Type a string to encrypt: ")
columns = int(raw_input("How many table columns would you like: "))
split = [before[i:i+columns] for i in range(0, len(before), columns)]
rows = len(split)
after = []
for i in range(0, columns):
for j in range(0,rows):
after.append(split[j][i])
print(after)
Update 2: If you want to pad the input using spaces, just add this line:
before += " " * (columns - len(before) % columns)
You'll end up with this code:
def encrypt():
before = raw_input("Type a string to encrypt: ")
columns = int(raw_input("How many table columns would you like: "))
before += " " * (columns - len(before) % columns)
split = [before[i:i+columns] for i in range(0, len(before), columns)]
rows = len(split)
after = []
for i in range(0, columns):
for j in range(0,rows):
after.append(split[j][i])
print ''.join(after)
Example output:
Type a string to encrypt: hello world
How many table columns would you like: 4
hore llwdlo
2 Comments
The errors was caused by rows not guaranteed to be regular and the string length not being divisible by the number of columns all the time.
(I don't have enough reputation to post images, sorry)
https://i.sstatic.net/kaJJo.png
The first grid will not work, as there are 4 empty spaces after the second '!' that we cannot access. The second grid will work.
range(n)is the same asrange(0, n). the 0 is implicitiyou iterate over the columns and withjover each substring. So you should use them for that.