I get IndexError when compiling python code - python

I am writing a cryptosystem program in python that involves to shift left the key
I get "IndexError: string index out of range" when I compile the code.
Below is the function of left shifting
def shift_left(key):
s = ""
for i in range(1,len(key)):
print(i)
s = s + key[i]
s = s + key[0]
key = s
s = ""
return key
I added the command print(i) to know for which value of i the index is out of range
It always print 1 and 2
So the problem occurs when i = 2
I have no idea why it's out of range

def shift_left(key):
s = ""
for i in key:
print(i)
s = s + i
s = s + key[0]
key = s
s = ""
return key

Related

How can I exclude something if a specific string is provided in the output?

So I wrote this code with the help of Stack Overflow users that transfers points to an individual based on whether "++" or "--" appears behind an individual.
def get_name(input):
return input.replace("+", "").replace("-", "")
def keep(actions: list):
g = {}
for s in actions:
if '->' in s:
names = s.split('->')
g[names[1]] = g[names[0]]
g[names[0]] = 0
else:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
return {x:g[x] for x in g if g[x] != 0}
print(keep(["Jim++", "John--", "Jeff++", "Jim++", "John--", "John->Jeff",
"Jeff--", "June++", "Home->House"]))
So most of the program is alright, however, when I put "Home->House" into it, it returns a KeyError. I kinda understand why it does that, but I'm clueless as to how to fix that...
I tried browsing the internet for solutions but all they recommended was to use .get(), which doesn't really help me solve the issue.
How can I make my output just neglect if like an element doesn't have "++" or "--" in it... like how can I make sure if an input is just "Jim" instead of "Jim++" or "Jim--", the function would just neglect it...
So in my example, if the input for keep is
["Jim++", "John--", "Jeff++", "Jim++", "John--", "John->Jeff", "Jeff--", "June++", "Home->House "]
the output would be
{'Jeff': -2, 'June': 1, 'Jim': 2}
instead of KeyError
You get KeyError because g[names[1]] = g[names[0]] is trying to access element in dictionary that isn't there. You get the same issue with simple print(keep(["John->Jeff"])), because there are no ++ or -- actions executed first to initialise those keys ("John" and "Jeff" in g
Based on your desired output you want to ignore such actions that are for non existing keys.
Add if names[1] in g and names[0] in g: into your keep implementation i.e.
Edit: also g[names[1]] = g[names[0]] needs to change to g[names[1]] += g[names[0]] to product correct outcome.
def get_name(input):
return input.replace("+", "").replace("-", "")
def keep(actions: list):
g = {}
for s in actions:
if "->" in s:
names = s.split("->")
if names[1] in g and names[0] in g:
g[names[1]] += g[names[0]]
g[names[0]] = 0
else:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
return {x: g[x] for x in g if g[x] != 0}
In your code, when you get Home->House, it's the first appearence of both the keys. That's why you get KeyError when you try to execute g[names[1] = g[names[0]]: g[names[0]] is g['Home'], but this entry doesn't exist in your dict. You can simply solve swapping the order of the lines:
if '->' in s:
names = s.split('->')
g[names[0]] = 0
g[names[1]] = g[names[0]]
To neglect strings which haven't the "++" or the "--", you can simply add an if before performing get_name(s):
else:
if '++' in s or '--' in s:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
This code returns the expected output
Your code actually does ignore examples which do not include "++" or "--" in the else-section of your function.
The KeyError occurs because neither "Home" nor "House" appear in the List of input strings before you try to move the entry of "Home" into the entry of "House". Because the keys "Home" and "House" are not in your dictionary g you get a KeyError.
Below is a solution which does what you want.
def get_name(input):
return input.replace("+", "").replace("-", "")
def keep(actions: list):
g = {}
for s in actions:
if '->' in s:
names = s.split('->')
if names[0] in g.keys() and names[1] in g.keys():
g[names[1]] = g[names[0]] + g[names[1]]
g[names[0]] = 0
else:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
return {x:g[x] for x in g if g[x] != 0}
print(keep(["Jim++", "John--", "Jeff++", "Jim++", "John", "John--", "John->Jeff",
"Jeff--", "June++", "Home->House"]))
I added a check to make sure the keys exist in dict d when the input string includes a "->".
In order to get the output you indicated in your question, which includes 'Jeff': -2, you also need to make sure to add the value of the original key to the value of the new key. This is done in line
g[names[1]] = g[names[0]] + g[names[1]]
Otherwise the output will say 'Jeff': -3 as the input string "Jeff++" will be ignored.

Permutation(swapping binary using a key order)

What I want is to reorder this '01110100' by a key 41325768 ,
the expected result: 11100010 , code result:10110010.
def perm(block, key):
block = list(block)
key = list(map(int, str(key))) # converting key to list
key = [x - 1 for x in key] # subtracting 1 from each (zero based)
new_key = [key[i] for i in key]
block = [block[i] for i in new_key]
block = "".join(block)
return block
so I added this line new_key = [key[i] for i in key] to fix the issue but the result so close:11100100
No idea what to do to fix it...😥
The difference between what you want and what you have is the difference in interpretation of the key. For example regarding the "4" at the start of your key, you want that to mean "put the first character in 4th position" but it's being used to say "take the first character from the fourth position".
You can change to the intended action by
def perm(block, key):
new_block = [" "]*len(key)
for ix, kval in enumerate(key):
new_block[int(kval)-1] = block[ix]
return "".join(new_block)
keyc = input('Permutation key: ')
plain = input('String to encode: ')
print ('\nResult: ', perm(str(plain),str(keyc)))
Input/output:
Permutation key: 41325768
String to encode: 01110100
Result: 11100010
I think I understood what means but your approach is wrong, here is a simple version of your code I made.
bi_s = '01110100'
key_s = '41325768'
new_bi = ''
bi_list = list(map(int, str(bi_s)))
key_list = list(map(int, str(key_s)))
print("The New Key")
for i in key_list:
# print(bi_list[i-1], end='', flush=True)
new_bi += str(bi_list[i-1])
print(new_bi)
print("The Origin Key")
for i in bi_list:
print(i, end='', flush=True)
the output :
The New Key
10110010
The Origin Key
01110100

Improving Encryption Algorithm

Recently I had started this assignment just meant for school purposes but I have recently decided that I would want to continue this as a new project and wanted suggestions on how I can improve my algorithm. I had an idea where I wanted the encryption key to change per character to make my encryption more secure but I have been having difficulty with this as It wouldn't work out and I couldnt decrypt my final encrypted text
Here is the encrypter:
text = input('Enter A Word : ') ##This asks a user for an input of text integers etc
encrypt = '' ##Empty Value used to store the encrypted value
temp = '' ##Empty temp value replaced at every iteration of the encryption process
temp2 =0 ##Empty temp value replaced at every iteration of the encryption process
temp_val=0
temp_char=''
rtext= text[::-1]
key=int(input('Enter your key (Your Encrypted Sentence Will Further be Encrypted Later) : '))##key used to shift the letters
for a in range (0,len(rtext)):
if len(rtext) % 2==0:
hlength=len(rtext)/2
else:
hlength=(len(rtext)+1)/2
print(hlength)
for i in range(int(hlength),len(rtext)):
##Rearranges text in a caps switch
if str.islower(rtext[i])==True:
temp=temp+str.upper(rtext[i])
elif str.isupper(rtext[i])==True:
temp=temp+str.lower(rtext[i])
else:
temp=temp+rtext[i]
for b in range(0,int(hlength)):
##Rearranges text in a caps switch
if str.islower(rtext[b])==True:
temp=temp+str.upper(rtext[b])
elif str.isupper(rtext[b])==True:
temp=temp+str.lower(rtext[b])
else:
temp=temp+rtext[b]
for j in range(0,len(temp)):
temp_val=0
temp2=0
temp_val=ord(temp[j])
temp2=temp2+temp_val+int(key)
temp_char=temp_char+chr(temp2)
encrypt=temp_char
print(encrypt)
print(temp)
print(temp2)
The Decrypter:
text = input('Enter A Word : ') ##This asks a user for an input of text integers etc
decrypt = '' ##Empty Value used to store the encrypted value
order=0
characters=''
temp=''
rtext=text[::-1]
key=int(input('Enter your key (decryption) : '))##key used to shift the letters
for i in range (0,len(rtext)):
order=0
order=order+ord(rtext[i])-int(key)
characters=characters+chr(order)
for a in range (0,len(rtext)):
if len(rtext) % 2==0:
hlength=len(rtext)/2
else:
hlength=(len(rtext)+1)/2
for j in range (int(hlength),len(characters)):
if str.islower(characters[j])==True:
temp=temp+str.upper(characters[j])
elif str.isupper(characters[j])==True:
temp=temp+str.lower(characters[j])
else:
temp=temp+characters[j]
for b in range (0,int(hlength)):
if str.islower(characters[b])==True:
temp=temp+str.upper(characters[b])
elif str.isupper(characters[b])==True:
temp=temp+str.lower(characters[b])
else:
temp=temp+characters[b]
print(temp)
I specifically want to change the variable key.
ord() - Turns characters into its Ascii equivalent
chr() - Turns Ascii numbers into its character equivalent
rtext - gets the inverse of the users input
If we simplify the code in the encryptor a little, we get:
def encrypt_text(text: str, key: int):
print("TEXT:", text, "KEY:", key)
temp = ''
temp2 = 0
temp_val = 0
temp_char = ''
rtext = text[::-1]
print("RTEXT:", rtext)
hlength = len(rtext) // 2 + len(rtext) % 2 # half, round up
print("HLENGTH:", hlength)
for i in range(hlength, len(rtext)):
# Rearrange text in a caps switch
if rtext[i].islower():
temp += rtext[i].upper()
elif rtext[i].isupper():
temp += rtext[i].lower()
else:
temp += rtext[i]
print("TEMP:", temp)
for b in range(0, int(hlength)):
# Rearrange text in a caps switch
if rtext[b].islower():
temp += rtext[b].upper()
elif rtext[b].isupper():
temp += rtext[b].lower()
else:
temp += rtext[b]
for j in range(len(temp)):
temp_val = 0
temp2 = 0
temp_val = ord(temp[j])
temp2 = temp2 + temp_val + int(key)
temp_char = temp_char + chr(temp2)
encrypt = temp_char
print("ENCRYPT:", encrypt)
print("TEMP:", temp)
print("TEMP2:", temp2)
return encrypt
text = "hello world"
key = 42
print("ENCRYPTED:", encrypt_text(text, key))
I've put it inside a function (and added some print statements), so it becomes easier to work with while developing. The code is essentially the same as yours, except
for a in range (0,len(rtext)):
if len(rtext) % 2==0:
hlength=len(rtext)/2
else:
hlength=(len(rtext)+1)/2
is replaced by
hlength = len(rtext) // 2 + len(rtext) % 2 # half, round up
which gives the same result (except hlength is an integer).
Your first two for loops do the same operation (switches case on a string). We can write a function for that:
def swap_case(str):
res = ''
for ch in str:
if ch.islower():
res += ch.upper()
elif ch.isupper():
res += ch.lower()
else:
res += ch
return res
and now we can replace the first two for loops with calls to our function:
temp += swap_case(rtext[hlength:len(rtext)]) # or shorter rtext[hlength:]
temp += swap_case(rtext[0:hlength]) # or shorter rtext[:hlength]
it just happend that .swapcase() is already a string method, so we didn't really need our swap_case function, and could just write:
temp += rtext[hlength:].swapcase()
temp += rtext[:hlength].swapcase()
Your third for-loop:
for j in range(len(temp)):
temp_val = 0 # this value is not used (it's overwritten 2 lines down)
temp2 = 0
temp_val = ord(temp[j])
temp2 = temp2 + temp_val + int(key) # temp2 is always zero at this point
temp_char = temp_char + chr(temp2)
encrypt = temp_char
can be simplified to (the initial value of temp_char is set to the empty string above):
for j in range(len(temp)): # for each index position (j)
temp_val = ord(temp[j]) # use the character at from temp at index j
temp2 = temp_val + int(key) # key is already an int from your: key=int(input('Enter your key (decryption) : '))
temp_char += chr(temp2)
encrypt = temp_char # hmm... just overwriting encrypt on every iteration
the comments mean that it could be even simpler:
encrypt = ''
for character in temp:
temp_val = ord(character)
temp2 = temp_val + key
encrypt += chr(temp2)
This leaves us with (the comments enumerate the steps taken):
def encrypt_text(text: str, key: int):
temp = ''
rtext = text[::-1] # (1) reverse the string
hlength = len(rtext) // 2 + len(rtext) % 2 # (2) split the string on hlength
second_part = rtext[hlength:].swapcase() # .. and swap case on the parts
first_part = rtext[:hlength].swapcase()
temp += second_part # (3) and put the second part..
temp += first_part # .. before the first part
encrypt = ''
for character in temp:
temp_val = ord(character)
temp2 = temp_val + key # (4) add key to every character
encrypt += chr(temp2)
return encrypt
to decrypt a string encrypted with this function, we need to do the operations "backwards and opposite":
def decrypt_text(encrypted, key):
temp = ''
for ch in encrypted:
temp += chr(ord(ch) - key) # subtract key from every character (4)
hlength = len(encrypted) // 2 + len(encrypted) % 2
half = len(encrypted) - hlength # the split point is a mirror image of what it is in encrypt_text (3)
rtext = ''
rtext += temp[half:].swapcase() # re-assemble the string and swap case (2)
rtext += temp[:half].swapcase()
text = rtext[::-1] # finally reverse (1)
return text
The standard way of using longer keys (similar to your one-key-per-character), is to use the xor function, which in Python is written as ^ (pronounced either 'hat' or 'xor'), as in:
a ^ b # true if either a, or b are true, but not both
Here is some background on how it works, although you don't really need to understand this to use it...
This operator work on bits. To see what is happening, lets define a
function to print the bit representation of an integer (you don't need
to understand this):
def bits(n):
return bin(n)[2:].zfill(4)
then we have we can show the bit patterns of integers 5 and 9, and the
operation 5 ^ 9:
bits(5) => 0101
bits(9) => 1001
--------------------
bits(5 ^ 9) => 1100
====================
if you look at the bit patterns, there is a 1 in the result where
there is exactly one 1 in the column above, so from left to right (0 ^
1 = 1, 1 ^ 0 = 1, 0 ^ 0 = 0, and 1 ^ 1 = 0).
Knowing the above, you can verify that for any number k ^ k == 0,
and n ^ 0 == n, and therefore n ^ k ^ k == n.
The useful thing about xor is that for any number n:
n ^ key ^ key == n
ie. xor-ing the number with key, twice, gives you back the number.
Let's use this to encrypt (zip(text, key) returns one character from text and key at a time, in lock-step, until one of them is "used up"):
def encrypt_xor(text: str, key: str):
if len(key) < len(text):
# key must be longer because of how zip works..
raise ValueError("Key must be at least as long as text")
res = ''
for ch, k in zip(text, key):
res += chr(ord(ch) ^ ord(k))
return res
if you try to print(encrypt_text('hello', 'world')) you'll get gibberish printed to your screen (since the value you get by xor-ing two characters isn't necessarily printable). The cool thing about xor is that the decrypt function is exactly the same as the encrypt function, so encrypting twice gives you the original value:
text = 'hello'
key = 'world'
cipher = encrypt_xor(text, key) # encrypted text is often called cipher
print(encrypt_xor(cipher, key)) # prints 'hello'
You can use a similar structure for shift-type encryption (but without the convenience that the decrypt function is the same as the encrypt), e.g.:
def encrypt_shift(text: str, key: str):
res = ''
for ch, k in zip(text, key):
res += chr(ord(ch) + ord(k)) # add the char from the key
return res
def decrypt_shift(text: str, key: str):
res = ''
for ch, k in zip(text, key):
res += chr(ord(ch) - ord(k)) # subtract the char from the key
return res
text = 'hello'
key = 'world'
cipher = encrypt_shift(text, key)
print(decrypt_shift(cipher, key)) # prints 'hello
to avoid the unpleasantness of needing a key that is longer than the text, we can start using the key from the beginning again if there is more text left. The itertools.cycle(..) function does this for us:
import itertools
def encrypt_shift(text: str, key: str):
res = ''
for ch, k in zip(text, itertools.cycle(key)):
res += chr(ord(ch) + ord(k))
return res
def decrypt_shift(text: str, key: str):
res = ''
for ch, k in zip(text, itertools.cycle(key)):
res += chr(ord(ch) - ord(k))
return res
now
text = 'hello world'
key = 'world'
cipher = encrypt_shift(text, key)
print(decrypt_shift(cipher, key)) # prints 'hello world' (not just 'hello' -- the first len(key) characters)
This can be plugged into the encrypt_text and decrypt_text functions from the other answer:
def encrypt_text(text: str, key: str): # key is now a string
temp = ''
rtext = text[::-1] # (1) reverse the string
hlength = len(rtext) // 2 + len(rtext) % 2 # (2) split the string on hlength
second_part = rtext[hlength:].swapcase() # .. and swap case on the parts
first_part = rtext[:hlength].swapcase()
temp += second_part # (3) and put the second part..
temp += first_part # .. before the first part
encrypt = encrypt_shift(temp, key) # (4) shift each char using key
return encrypt
and
def decrypt_text(encrypted, key):
temp = decrypt_shift(encrypted, key) # unshift each char using key
hlength = len(encrypted) // 2 + len(encrypted) % 2
half = len(encrypted) - hlength # the split point is a mirror image of what it is in encrypt_text (3)
rtext = ''
rtext += temp[half:].swapcase() # re-assemble the string and swap case (2)
rtext += temp[:half].swapcase()
text = rtext[::-1] # finally reverse (1)
return text

Why am i getting an error when calling main()?

so here's some code i have that is supposed to take text entered by the user and create a dictionary. Can anyone tell me why I get a traceback error when I call the function main()?
def build_index(text):
index = {}
words = text.split()
position = 0
for x in text:
if x.isalpha() == False and x.isdigit() == False:
text.join(x)
else:
text.replace(x,'')
while position < len(words):
nextword = words[position]
if nextword in index:
ref = index[nextword]
ref.append(position)
index[nextword] = ref
else:
list = []
list.append(position)
index[nextword] = list
position += 1
def displayindex(index):
keys = sorted(index.keys())
for key in keys:
print(key + ':' + str(index[key]))
def main():
text = input("enter text")
build_index(text)
displayindex(index)
main()
The traceback error contents depends on which version of Python you're running your code in. In Python 3.x, the traceback explains why it's producing the error:
Traceback (most recent call last):
File "./prog.py", line 37, in
File "./prog.py", line 36, in main
NameError: name 'index' is not defined
TLDR: Need to add/change only 3 lines of code. See comments in code below
The NameError is telling us that it doesn't know what the name index refers to, because it's out of the scope of the main method and hasn't gotten defined yet. You could create the global instance of the index variable as mentioned in MeterLongCat's answer, but since index does get created and defined when we call build_index, we can just return index after that method call, save its return value, then pass it to the displayindex function, as follows.
OTOH, in Python 2, as MeterLongCat points out, you're wanting to get a string from the user, which is not what input is for, you want raw_input.
def build_index(text):
index = {}
words = text.split()
position = 0
for x in text:
if x.isalpha() == False and x.isdigit() == False:
text.join(x)
else:
text.replace(x,'')
while position < len(words):
nextword = words[position]
if nextword in index:
ref = index[nextword]
ref.append(position)
index[nextword] = ref
else:
list = []
list.append(position)
index[nextword] = list
position += 1
return index # Return the index
def displayindex(index):
keys = sorted(index.keys())
for key in keys:
print(key + ':' + str(index[key]))
def main():
text = raw_input("enter text") # Use raw_input
index = build_index(text) # Assign the index
displayindex(index)
main()
I was able to get rid of the traceback error by changing input to raw_input (for Python 2.7). You have other errors though, for example index in method main isn't defined. The following works:
index = {}
def build_index(text):
global index
words = text.split()
position = 0
for x in text:
if x.isalpha() == False and x.isdigit() == False:
text.join(x)
else:
text.replace(x,'')
while position < len(words):
nextword = words[position]
if nextword in index:
ref = index[nextword]
ref.append(position)
index[nextword] = ref
else:
list = []
list.append(position)
index[nextword] = list
position += 1
def displayindex(index):
keys = sorted(index.keys())
for key in keys:
print(key + ':' + str(index[key]))
def main():
global index
text = raw_input("enter text")
build_index(text)
displayindex(index)
main()

How could I write this function the pythonic way? [duplicate]

I have a string like '....(((...((...' for which I have to generate another string 'ss(4)h5(3)ss(3)h2(2)ss(3)'.
'.' corresponds to 'ss' and the number of continous '.' is in the bracket.
'(' corresponds to 'h5' and the number of continuos '(' is in the bracket.
Currently I'm able to get the output 'ss(4)h5(3)ss(3)' and my code ignores the last two character sequences.
This is what I have done so far
def main():
stringInput = raw_input("Enter the string:")
ssCount = 0
h5Count = 0
finalString = ""
ssString = ""
h5String = ""
ssCont = True
h5Cont = True
for i in range(0, len(stringInput), 1):
if stringInput[i] == ".":
h5Cont = False
if ssCont:
ssCount = ssCount + 1
ssString = "ss(" + str(ssCount) + ")"
ssCont = True
else:
finalString = finalString + ssString
ssCont = True
ssCount = 1
elif stringInput[i] == "(":
ssCont = False
if h5Cont:
h5Count = h5Count + 1
h5String = "h5(" + str(h5Count) + ")"
h5Cont = True
else:
finalString = finalString + h5String
h5Cont = True
h5Count = 1
print finalString
main()
How to modify the code to get the desired output?
I don’t know about modifying your existing code, but to me this can be done very succinctly and pythonically using itertools.groupby. Note that I’m not sure if the 'h2' in your expected output is a typo or if it should be 'h5', which I’m assuming.
from itertools import chain, groupby
string = '....(((...((...'
def character_count(S, labels): # this allows you to customize the labels you want to use
for K, G in groupby(S):
yield labels[K], '(', str(sum(1 for c in G)), ')' # sum() counts the number of items in the iterator G
output = ''.join(chain.from_iterable(character_count(string, {'.': 'ss', '(': 'h5'}))) # joins the components into a single string
print(output)
# >>> ss(4)h5(3)ss(3)h5(2)ss(3)
#Kelvin 's answer is great, however if you want to define a function yourself, you could do it like this:
def h5ss(x):
names = {".": "ss", "(": "h5"}
count = 0
current = None
out = ""
for i in x:
if i == current:
count += 1
else:
if current is not None:
out += "{}({})".format(names[current], count)
count = 1
current = i
if current is not None:
out += "{}({})".format(names[current], count)
return out

Categories

Resources