String index out of range (Python) - python

I'm writing a program to encode, decode and crack with the Caesar Cipher.
I have this function that shifts the letters in a string along by a specified amount:
def shift(data, shifter):
alphabet = "abcdefghijklmnopqrstuvwxyz"
data = list(data)
counter = 0 #  we will use this to modify the list while we iterate over it
for letter in data:
letter = letter.lower()
if letter not in alphabet:
counter += 1
continue
lPos = alphabet.find(letter)
if shifter >= 0:
shiftedPos = lPos + (0 - shifter)
else:
shiftedPos = lPos + abs(shifter)
if shiftedPos >= len(alphabet) - 1: shiftedPos -= len(alphabet)
data[counter] = alphabet[shiftedPos] #  update the letter
counter += 1 # advance
data = ''.join(data) # make it into a string again
return data
And I have this function to crack a ciphered string:
def crack(decryptor=None, tries=None):
if decryptor is None and tries is None:
task = getValidInput("Get data from a [f]ile or [s]tdin? >", "Please give either 'f' or 's'.", 'f', 's')
if task == "f": # it's a file
dataFile = getValidFile() # get an open file object
data = dataFile.read() # get the data from the text file. hopefully it's ascii text!
elif task == "s": # we need to get data from stdin
data = input("Enter data to crack >")
tries = getValidInt("Enter tries per sweep >")
else:
data = decryptor
retry = True
shifter = 0
while retry:
for i in range(0, tries):
oput = "Try " + str(i) + ": "
posData = shift(data, shifter)
negData = shift(data, 0 - shifter)
# semitry 1 - positive
oput += posData + ", "
# semitry 2 - negative
negData = ''.join(negData) # make it into a string again
oput += negData
print(oput)
shifter += 1
doRetry = getValidInput("Keep trying (y/n)? > ", "Invalid!", 'y', 'n')
if doRetry == 'n': retry = False
However, after selecting 'y' to continue a few times, I get the following IndexError:
Traceback (most recent call last):
File "CeaserCypher.py", line 152, in <module>
crack()
File "CeaserCypher.py", line 131, in crack
negData = shift(data, 0 - shifter)
File "CeaserCypher.py", line 60, in shift
print(alphabet[shiftedPos])
IndexError: string index out of range
Why am I getting this error and how can I fix it?

IndexError means that the index you are trying to access does not exist. In a string, that means you're trying to get a character from the string at a given point. If that given point does not exist, then you will be trying to get a character that is not inside of the string.
"0123456"[7] tries to get the 7th character in the string, but that index does not exist so "IndexError" is raised.
All valid indexes on a string are less than the length of the string (when you do len(string)). In your case, alphabet[shiftedPos] raises IndexError because shiftedPos is greater than or equal to the length of the string "alphabet".
To my understanding, what you want to do is loop back over the string when you go out of bounds like this. "z" (character 25) gets incrimented by say 2 and becomes character 27. You want that to now become character 2 (letter "b") in this case. Hence, you should use modulo. replace "alphabet[shiftedPos]" with "alphabet[shiftedPos%len(alphabet)]" and I believe this will solve this problem.
Modulo, btw, divides a number by n and gives you the remainder. Effectively, it will subtract n until the number is less than n (so it will always be in the range you want it to be in).

Related

CS50 DNA works for small.csv but not for large

I am having problems with CS50 pset6 DNA. It is getting all the right values and gives correct answers when I use the small.csv file but not when I use the large one. I have been going through it with debug50 for over a week and can't figure out the problem. I assume the problem is somewhere in the loop through the samples to find the STRS but I just don't see what it is doing wrong when walking through it.
If you are unfamiliar with CS50 DNA problemset, the code is supposed to look through a dna sequence (argv[1]) and compare it with a CSV file containing people DNA STRs to figure out which person (if any) it belongs to.
Note; My code fails within the case; (Python dna.py databases/large.csv sequences/5.txt) if this helps.
from sys import argv
from csv import reader
#ensures correct number of arguments
if (len(argv) != 3):
print("usage: python dna.py data sample")
#dict for storage
peps = {}
#storage for strands we look for.
types = []
#opens csv table
with open(argv[1],'r') as file:
data = reader(file)
line = 0
number = 0
for l in data:
if line == 0:
for col in l:
if col[2].islower() and col != 'name':
break
if col == 'name':
continue
else:
types.append(col)
line += 1
else:
row_mark = 0
for col in l:
if row_mark == 0:
peps[col] = []
row_mark += 1
else:
peps[l[0]].append(col)
#convert sample to string
samples = ""
with open(argv[2], 'r') as sample:
for c in sample:
samples = samples + c
#DNA STR GROUPS
dna = { "AGATC" : 0,
"AATG" : 0,
"TATC" : 0,
"TTTTTTCT" : 0,
"TCTAG" : 0,
"GATA" : 0,
"GAAA" : 0,
"TCTG" : 0 }
#go through all the strs in dna
for keys in dna:
#the longest run of sequnace
longest = 0
#the current run of sequances
run = 0
size = len(keys)
#look through sample for longest
i = 0
while i < len(samples):
hold = samples[i:(i + size)]
if hold == keys:
run += 1
#ensure the code does not go outside len of samples
if ((i + size) < len(samples)):
i = i + size
continue
if run > longest:
longest = run
run = 0
i += 1
dna[keys] = longest
#see who it is
positive = True
person = ''
for key in peps:
positive = True
for entry in types:
x = types.index(entry)
test = dna.get(entry)
can = int(peps.get(key)[x])
if (test != can):
positive = False
if positive == True:
person = key
break
if person != '':
print(person)
else:
print("No match")
Problem is in this while loop. Look at this code carefully.
while i < len(samples):
hold = samples[i:(i + size)]
if hold == keys:
run += 1
#ensure the code does not go outside len of samples
if ((i + size) < len(samples)):
i = i + size
continue
if run > longest:
longest = run
run = 0
i += 1
You have a missing logic here. You are supposed to check the longest consecutive DNA sequence. So when you have a repetition of dna sequence back to back, you need to find how many times it is repeated. When it is no longer repeated, only then, you need to check if this is the longest sequence.
Solution
You need to add else statement after if hold==keys: statement. This would be the right fix;
while i < len(samples):
hold = samples[i:(i + size)]
if hold == keys:
run += 1
#ensure the code does not go outside len of samples
if ((i + size) < len(samples)):
i = i + size
continue
else: #only if there is no longer sequence match, check this.
if run > longest:
longest = run
run = 0
else: #if the number of sequence match is still smaller then longest, then make run zero.
run = 0
i += 1
earik87 is absolutely right! Just I like to add, the code is missing an = to work for all the cases especially when you have redundant sequences.
while i < len(samples):
hold = samples[i:(i + size)]
if hold == keys:
run += 1
#ensure the code does not go outside len of samples **( I added =)**
if ((i + size) <= len(samples)):
i = i + size
continue
else: #only if there is no longer sequence match, check this.
if run > longest:
longest = run
run = 0
else: #if the number of sequence match is still smaller then longest, then make run zero.
run = 0
i += 1

How to fix a String index out of range exception in Python

There is some issue with my python code. I am making a program that finds the occurrences of the letter A in a word and if that letter is found and the next letter is not the letter A the A is swapped with the next letter.
As an example TAN being TNA but WHOA staying as WHOA
AARDVARK being ARADVRAK
The issue is when I input ABRACADABRA I get a string index out of range exception. Before I had that exception I had the word that prints it as BRACADABRIi'm not sure why if I have to add another loop in my program.
If you guys also have anymore efficient way to run the code then the way I have please let me know!
def scrambleWord(userInput):
count = 0
scramble = ''
while count < len(userInput):
if userInput[count] =='A' and userInput[count+1] != 'A':
scramble+= userInput[count+1] + userInput[count]
count+=2
elif userInput[count] != 'A':
scramble += userInput[count]
count+=1
if count < len(userInput):
scramble += userInput(len(userInput)-1)
return scramble
#if a is found switch the next letter index with a's index
def main():
userInput = input("Enter a word: ")
finish = scrambleWord(userInput.upper())
print(finish)
main()
When you get to the end of the string and it is an 'A' your program is then asking for the next character which is off the end of the string.
Change the loop so it doesn't include the last character:
while count < len(userInput)-1:
if ...
You can modify your code as below:
def scrambleWord(userInput):
count = 0
scramble = ''
while count < len(userInput):
if count < len(userInput)-1 and userInput[count] =='A' and userInput[count+1] != 'A':
scramble+= userInput[count+1] + userInput[count]
count+=2
else:
scramble += userInput[count]
count+=1
return scramble
You are not checking the condition (count < len(userInput)-1) when logic tries to check for A's occurrence and swap with next letter. It throws string index out of range exception.
The issue arises in your code when last character in input is 'A'.
This is because your first if in the loop tries to access 'count + 1' character during last iteration.
And since there's no character at that position, you get index error.
The simplest solution would be to make a separate if condition for the same.
Updated snippet for while loop might look like this -
# while start
while count < len_: # len_ is length of input
if count + 1 >= len_:
break # break outta loop, copy last character
current = inp[count]
next_ = inp[count + 1]
if current == 'A':
op += ( next_ + current) # op is result
count += 1
else:
op += current
# increment counter by 1
count += 1
# rest of the code after while is same
Another small issue in your code is while copying last character ( after loop ends ), you should use [ ] instead of ( ) to refer last character in input string.
Just for fun :
from functools import reduce
def main():
word = input("Enter a word: ").lower()
scramble = reduce((lambda x,y : x[:-1]+y+'A' \
if (x[-1]=='a' and y!=x[-1]) \
else x+y),word)
print(scramble.upper())
main()

Convert a number into its respective letter in the alphabet - Python 2.7

I am currently working on a python project to take a string of text, encrypt the text by adding a keyword to it and then outputting the result. I currently have all the features of this program operational except converting the numerical value back into text.
For example, the raw text will be converted into a numerical value, for instance [a, b, c] will become [1, 2, 3].
Currently I have no ideas of how to correct this issue and would welcome any help, my current code is as follows:
def encryption():
print("You have chosen Encryption")
outputkeyword = []
output = []
input = raw_input('Enter Text: ')
input = input.lower()
for character in input:
number = ord(character) - 96
output.append(number)
input = raw_input('Enter Keyword: ')
input = input.lower()
for characterkeyword in input:
numberkeyword = ord(characterkeyword) - 96
outputkeyword.append(numberkeyword)
first = output
second = outputkeyword
print("The following is for debugging only")
print output
print outputkeyword
outputfinal = [x + y for x, y in zip(first, second)]
print outputfinal
def decryption():
print("You have chosen Decryption")
outputkeyword = []
output = []
input = raw_input('Enter Text: ')
input = input.lower()
for character in input:
number = ord(character) - 96
output.append(number)
input = raw_input('Enter Keyword: ')
input = input.lower()
for characterkeyword in input:
numberkeyword = ord(characterkeyword) - 96
outputkeyword.append(numberkeyword)
first = output
second = outputkeyword
print("The following is for debuging only")
print output
print outputkeyword
outputfinal = [y - x for x, y in zip(second, first)]
print outputfinal
mode = raw_input("Encrypt 'e' or Decrypt 'd' ")
if mode == "e":
encryption()
elif mode == "d":
decryption()
else:
print("Enter a valid option")
mode = raw_input("Encrypt 'e' or Decrypt 'd' ")
if mode == "e":
encryption()
elif mode == "d":
decryption()
Although Your Question is not quite clear I wrote a script which do encryption using Dictionary
plaintext = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') #Alphabet String for Input
Etext = list('1A2B3C4D5E6F7G8H90IJKLMNOP') """Here is string combination of numbers and alphabets you can replace it with any kinda format and your dictionary will be build with respect to the alphabet sting"""
def messageEnc(text,plain, encryp):
dictionary = dict(zip(plain, encryp))
newmessage = ''
for char in text:
try:
newmessage = newmessage + dictionary[char.upper()]
except:
newmessage += ''
print(text,'has been encryptd to:',newmessage)
def messageDec(text,encryp, plain):
dictionary = dict(zip(encryp,plain))
newmessage = ''
for char in text:
try:
newmessage = newmessage + dictionary[char.upper()]
except:
newmessage += ''
print(text,'has been Decrypted to:',newmessage)
while True:
print("""
Simple Dictionary Encryption :
Press 1 to Encrypt
Press 2 to Decrypt
""")
try:
choose = int(input())
except:
print("Press Either 1 or 2")
continue
if choose == 1:
text = str(input("enter something: "))
messageEnc(text,plaintext,Etext)
continue
else:
text = str(input("enter something: "))
messageDec(text,Etext,plaintext)
Suppose you've got a list of numbers like a = [1, 2, 3] and an alphabet: alpha = "abcdef". Then you convert this as follows:
def NumToStr(numbers, alpha):
ret = ""
for num in numbers:
try:
ret += alpha[num-1]
except:
# handle error
pass
return ret
Going by your question , you want to convert number back to text.
You can have two list declared for both the number and alphabet, such as:
num=[1,2,3....26]
alpa=['a','b','c'....'z']
and after that find the index/position from the num list and find the alphabet for that index in alpa list.
A couple of things. The first is to convert the coded values back to characters you can use the chr(i) command. Just remember to ad the 96 back on.
for code in outputfinal:
print chr(code + 96),
I tried 'aaa' as my plaintext and 'bbb' as my keyword and this printed 'ccc' which is what I think you are trying to do.
Another thing. The zip command iterates over two lists but (in my playing) only until one of the lists runs out of members. If your plaintext is 10 characters and your keyword is only 5 then only the first 5 characters of your plaintext get encrypted. You will need to expand or inflate your key to the length of your plaintext message. I played with something like this:
plaintext = ['h','e','l','l','o',' ','w','o','r','l','d']
keyword = ['f','r','e','d']
index = len(keyword) # point to the end of the key word
keyLength = len(keyword)
while index < len(plaintext): # while there are still letters in the plain text
keyword.append(keyword[index - keyLength]) # expand the key
index += 1
print keyword
for a,b in zip(plaintext, keyword):
print a, b
I hope this helps. Please let me know if I have misunderstood.

Error I can't fix while converting text to number in RSA algorithm using Python

I was tasked an assignment by my teacher to make a python program that can encrypt messages using the RSA algorithm. So to do that I had to create functions which convert text to numbers and then be able to manipulate those numbers and encrypt it. However to test if i have encrypted it correctly i also designed a decryption function which I also made at the bottom. All the functions between the encryption functions and Ascii functions are there to be used in other functions(At this point you would realize i like dividing my code into functions). The problem with my code is that i keep getting this error when i run it
Traceback (most recent call last):
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 127, in <module>
print(decrypt(Phi_N, e, encrypted))
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 115, in decrypt
return Num2Text(message)
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 58, in Num2Text
return Ascii2Text(Num2Ascii(message))
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 17, in Ascii2Text
text = text + chr(int(char))
ValueError: invalid literal for int() with base 10: '1.0'
>>> ================================ RESTART =================
I tested all the functions individually to see if they work and they do. I rechecked my encryption and decryption functions and they should work. For some reason i think my problem is with my decryption function as i think it is giving me the decimal number however i searched the internet on the algorithm and all the sites i looked uses the same algorithm. Please help me with finding the problem, I usually dont ask other people for help (This is my first ever question on StackOverFlow) and I've been hurting my head on this for 3 weeks.
import math
def Text2Ascii(message):
Ascii = ""
for count in range(len(message)):
if len(str(ord(message[count]))) <= 2:
Ascii = Ascii + "0" +str(ord(message[count])) + " "
else:
Ascii = Ascii + str(ord(message[count])) + " "
return Ascii
def Ascii2Text(message):
text = ""
char = ""
for count in range(len(message)):
if message[count] == " ":
text = text + chr(int(char))
char = ""
else:
char = char + message[count]
text = text + chr(int(char))
return text
def Ascii2Num(message):
Num = ""
for count in range(len(message)):
if message[count] != " ":
Num = Num + message[count]
return int(Num)
def Num2Ascii(message):
Ascii = ""
char = ""
message = str(message)
if len(message)%3 == 0:
for count in range(len(message)):
if (count+1)%3 == 0:
char = char + message[count]
Ascii = Ascii + char + " "
char = ""
else:
char = char + message[count]
else:
Ascii = "0"
for count in range(len(message)):
if (count+2)%3 == 0:
char = char + message[count]
Ascii = Ascii + char + " "
char = ""
else:
char = char + message[count]
return Ascii
def Text2Num(message):
return Ascii2Num(Text2Ascii(message))
def Num2Text(message):
return Ascii2Text(Num2Ascii(message))
def exponent(number, power):
answer = 1
for count in range(power):
answer = number*answer
return answer
def primeCheck(number):
if number == 2:
prime = True
elif number <= 1:
prime = False
else:
n = math.sqrt(number)
n = math.ceil(n)
for count in range(2,n+1):
if number%count == 0:
prime = False
break
else:
prime = True
return prime
def factors(number):
answer = []
if primeCheck(number)== True:
answer = [number,1]
else:
for count in range(1,number+1):
if number%count == 0:
answer.append(count)
return answer
def phi(number):
answer = number
if primeCheck(number) == True:
answer = number - 1
else:
factor = factors(number)
for count in factor:
for count2 in factors(count):
if count2 == count:
answer = answer -1
break
return answer
def encrypt(N,e):
message = input("What is the message you need encrypted?\n")
message = Text2Num(message)
encrypted = (message ** e)%N
return encrypted
def decrypt(Phi_N, e,encrypted):
k = 3
d = (Phi_N*k + 1)/e
message = encrypted ** d
return Num2Text(message)
p = int(input("Give me a prime number: "))
while primeCheck(p) == False:
p = int(input("Give me a prime number: "))
q = int(input("Give me a prime number: "))
while primeCheck(q) == False:
q = int(input("Give me a prime number: "))
N = q*p
Phi_N = (q-1)*(p-1)
e = int(input("Enter your encryption key: "))
encrypted = encrypt(N,e)
print(decrypt(Phi_N, e, encrypted))
That specific error that you're getting is due to combination of two factors:
The first, and probably easiest to address, is the way you are breaking up the string in Ascii2Text:
def Ascii2Text(message):
text = ""
char = ""
for count in range(len(message)):
**if message[count] == " ":** #<-- This line is not robust
text = text + chr(int(char))
char = ""
else:
char = char + message[count]
text = text + chr(int(char))
return text
The problem is that, with the way Num2Ascii is implemented, you are ending up with both a trailing space that causes you to try to cast empty string to an int at the end of the loop, and non-space characters earlier in your ascii-fied message. I would recommend either loudly choking on invalid characters, or at the very least not trying to append them to the "char" string that you try to cast to an int on the next line.
This brings us to the next problem: that you even have non-numeral characters in the first place. The decimal point causing the failure you see is coming from an incorrect calculation for d. d is supposed to be the modular multiplicative inverse of e, and it will always be an integer when calculated correctly. Yours, however, was coming up as a decimal more often than not when I ran this code. This is one of the bits of RSA that people choke on most often, since it involves manipulating the modulo operator in ways that people without a math background aren't typically taught. One of the more common, efficient ways to calculate this value is to use the Extended Euclidean algorithm.
A few more notes on things you will probably run into after addressing the two issues above:
e should be co-prime with and less than Phi_N, which you are not currently validating. Failure to do this can make it impossible to correctly calculate d (only few pairs of numbers have a modular multiplicative inverse). The easiest (though not strictly correct) way to do this would be to use a while loop beneath e like you did for the prime numbers, which checks that False == primeCheck(e) or e >= Phi_N. To be more correct, you'd have to write a coprime check that takes in both Phi_N and e, and asserts that they do not share a common factor besides 1.
The padded version of your message, m, must be less than N. Failure to do this may result in some messages being unrecoverable when you attempt to reverse the exponentiation during decryption. To more easily verify this, you may want to capture the message from the user before they select P and Q (they're more likely to change their numbers than the message they want to send), then add an additional gate after the two primes are provided which checks that N > m.

What is IndexError? I keep Getting It

Here is my code:
quote = input("Enter a Sentence: ")
a = len(quote)
counter = 0
counter1 = 0
reverse = a
print("The Length of the sentence is",a,"characters long!")
for x in range(0,a):
if str.isspace(quote[x]) == True:
counter = counter + 1
print("The Length of the sentence is",a - counter,"characters long (excluding space...)!")
for x in range(0,a):
if str.isupper(quote[x]) == True:
counter1 = counter1 + 1
print("The number of Upper Case Characters in the sentence is",counter1,"characters!")
print("The number of Lower Case Characters in the sentence is",a-counter1,"characters long!\n")
while reverse >= 1:
r = reverse
print(quote[r])
r = r - 1
It's aim is to find everything about that sentence, but the only problem is the 'While' loop at the bottom. It doesn't seem to work, and it's aim is to 'inverse' the sentence. However, it gives my an error which looks a bit like this:
Traceback (most recent call last):
File "C:\Documents and Settings\ususer\My Documents\Downloads\StringStuff.py", line 27, in <module>
print(quote[r])
IndexError: string index out of range
What Am I doing wrong? Please help!
Python is 0-indexed, so the first character of a string is str[0] and the last is str[len(str) - 1]. So, since you start with reverse = len(quote), at the end you are doing quote[len(quote)], which is one past the end of the string.
So, you should probably start with reverse = a - 1 and your loop at the end should look something like:
while reverse >= 0:
print(quote[reverse])
reverse = reverse - 1
You have run into the common problem that Python starts indexing from 0, but returns the lenght of a list as a valid integer counted from 1. This results that for any list, l[len(l)] will give you IndexError, because a list of length 10 will only have indexes 0...9.
All you have to do is to initialize reverse = len(quote)-1.
You also have to descend your loop variable inside the while loop, so use reverse-=1 instead of r=r-1.

Categories