Anyway to shorten this Python Caesar Cipher code into fewer lines? - python

def encrypt(text, key, direction):
if direction == 1: #The direction is either -1, or 1. If it is 1, it goes right. Otherwise, it will go left.
emptys=''
for x in text:
b = ord(x) #b is the individual characters after the ord() function
b+=key
if b<=122:
n = chr(b) #n is the converted letter of the encrypted ASCII number
emptys+=n
else:
o=b-90
q=chr(o)
emptys+=q
return emptys
else:
emptys=''
for x in text:
b = ord(x) #b is the individual characters after the ord() function
b=b-key
if b>=32:
n = chr(b) #n is the converted letter of the encrypted ASCII number
emptys+=n
else:
o=b+90
q=chr(o)
emptys+=q
return emptys

Your code as written blithely translates alphabetic characters to non-alphabetic and vice versa (e.g. encrypt('abc', 25, 1) gets 'z!"'). So it's wrong by most definitions of the traditional Caesar cipher, which should only modify alphabetic characters, and should rotate them within the alphabet.
That said, getting it right is easier than you're making it. The best approach is to avoiding rolling your own rotation code. Python already provides a really nice way to do one-to-one character mappings, str.translate. For example:
from string import ascii_letters, ascii_uppercase, ascii_lowercase
def encrypt(text, key, direction):
# direction == -1 is trivially converted to direction == 1 case
if direction == -1:
key = 26 - key
# On Py2, you'd use the string module's string.maketrans instead of
# str.maketrans
trans = str.maketrans(ascii_letters, ascii_lowercase[key:] + ascii_lowercase[:key] + ascii_uppercase[key:] + ascii_uppercase[:key])
return text.translate(trans)

Related

Shifting all the alphabets of a string by a certain step

input: ['baNaNa', 7] # string and step size
required output : 'utGtGt' # every character of string shifted backwards by step size
import ast
in_string = input()
lis = ast.literal_eval(in_string)
st = lis[0]
step = lis[1]
alphabets = 'abcdefghijklmnopqrstuvwxyz'
password = ''
for letter in st:
if letter in alphabets:
index_val = alphabets.index(letter) - (step)
password += alphabets[index_val]
print(password)
Output i am getting is 'utgtgt'. I want 'utGtGt'. Help on this would be appreciated a lot.
The string module has methods to create a transformation dictionary and a translate method to do exactly what you want:
st = "baNaNa"
step = 7
alphabets = 'abcdefghijklmnopqrstuvwxyz'
alph2 = alphabets.upper()
# lower case translation table
t = str.maketrans(alphabets, alphabets[-step:]+alphabets[:-step])
# upper case translation table
t2 = str.maketrans(alph2, alph2[-step:]+alph2[:-step])
# merge both translation tables
t.update(t2)
print(st.translate(t))
Output:
utGtGt
You give it the original string and an equal long string to map letters to and apply that dictionary using str.translate(dictionary).
The sliced strings equate to:
print(alphabets)
print(alphabets[-step:]+alphabets[:-step])
abcdefghijklmnopqrstuvwxyz
tuvwxyzabcdefghijklmnopqrs
which is what your step is for.
See Understanding slice notation if you never saw string slicing in use.
by processing each charater and checking it's cardinal no and making calculation accordingly help you to reach the result
def func(string, size):
if size%26==0:
size=26
else:
size=size%26
new_str = ''
for char in string:
if char.isupper():
if ord(char)-size<ord('A'):
new_str+=chr(ord(char)-size+26)
else:
new_str+=chr(ord(char)-size)
elif char.islower():
if ord(char)-size<ord('a'):
new_str+=chr(ord(char)-size+26)
else:
new_str+=chr(ord(char)-size)
return new_str
res =func('baNaNa', 7)
print(res)
# output utGtGt
Here's a simple solution that makes use of the % modulo operator to shift letters backwards.
It basically collects all of the letters in a reverse index lookup dictionary, so looking up letter positions is O(1) instead of using list.index(), which is linear O(N) lookups.
Then it goes through each letter and calculates the shift value from the letter index e.g. for the letter a with a shift value of 7, the calculation will be (0 - 7) % 26, which will give 19, the position of u.
Then once you have this shift value, convert it to uppercase or lowercase depending on the case of the original letter.
At the end we just str.join() the result list into one string. This is more efficient than doing += to join strings.
Demo:
from string import ascii_lowercase
def letter_backwards_shift(word, shift):
letter_lookups = {letter: idx for idx, letter in enumerate(ascii_lowercase)}
alphabet = list(letter_lookups)
result = []
for letter in word:
idx = letter_lookups[letter.lower()]
shifted_letter = alphabet[(idx - shift) % len(alphabet)]
if letter.isupper():
result.append(shifted_letter.upper())
else:
result.append(shifted_letter.lower())
return ''.join(result)
Output:
>>> letter_backwards_shift('baNaNa', 7)
utGtGt
I would probably go with #Patrick Artner's pythonic solution. I just showed the above implementation as a learning exercise :-).

python string.split() and loops

disclaimer im new to python
i need to split a string input send if to a function that substitutes a character in the string with a different character (like a substitution cipher) but i just dont know how to go about this
print('Welcome to the encryption protocol for top secret governemt cover ups')
string=input('whats your message?')
def encrypt(string):
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
index=0
while index < len(string):
letter=string[index]
im not really sure what im doing im really bad at python, this has had me stumped for 3 days now ive reviewed my course material and tried videos on youtube im probably just really really dumb
I think the key piece of knowledge you're missing is that strings are iterable. So you can do things like:
for c in "FOO":
print(c)
# prints "F\nO\nO\n"
And you can find the index of a character within a string with str.index. So you can build up your cyphertext like this:
alpha = "abcdefghijklmnopqrstuvwyz "
cypher = "pokmenliuytrw qazxcvsdfgbhn"
plaintext = "some string"
cyphertext = ""
for c in plaintext:
char_index = alpha.index(c)
cyphertext += cypher[char_index]
You can also iterate over things inline - this is called a comprehension. So to transform your string you can do this instead of using the for loop:
cyphertext = "".join(cypher[alpha.index(c)] for c in plaintext)
The example above uses the str.join function to concatenate each character of cyphertext.
Here is a solution that asks the question and then iterates through each letter, finding the index in the alpha key, and replacing it with the sub_alpha key equivalent.
Note this example also checks if it should be lowercase or uppercase.
EDIT: if the input character does not have a valid cipher, it doesn't get altered.
EDIT 2: expanded answer to convert both forwards and backwards.
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
def encrypt(in_char):
is_lower_case = in_char.islower()
index = alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return sub_alpha[index]
else:
return sub_alpha[index].upper()
def decrypt(in_char):
is_lower_case = in_char.islower()
index = sub_alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return alpha[index]
else:
return alpha[index].upper()
print('Welcome to the encryption protocol for top secret governemt cover ups')
input_str=input('whats your message? ')
output_str=""
for letter in input_str:
output_str += encrypt(letter)
print("Encrypted: ")
print(output_str)
input_str=""
for letter in output_str:
input_str+= decrypt(letter)
print("Decrypted: ")
print(input_str)

Conversion of string to upper case without inbuilt methods

I am trying to perform conversion from a lowercase to uppercase on a string without using any inbuilt functions (other than ord() and char()). Following the logic presented on a different thread here , I came up with this.
def uppercase(str_data):
ord('str_data')
str_data = str_data -32
chr('str_data')
return str_data
print(uppercase('abcd'))
However I am getting an error output: TypeError: ord() expected a character, but string of length 8 found.What am I missing here?
You need to execute ord() for each character of your input string. instead of the input string:
def uppercase(str_data):
return ''.join([chr(ord(char) - 32) for char in str_data if ord(char) >= 65])
print(uppercase('abcdé--#'))
>>> ABCDÉ
Without join:
def uppercase(str_data):
result = ''
for char in str_data:
if ord(char) >= 65:
result += chr(ord(char) - 32)
return result
print(uppercase('abcdé--#λ'))
>>> ABCDÉΛ
The best way, in my opinion is using a helper string, representing the alphabet, if you do not want to use chr() and ord():
def toUppercase(s):
alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
result = ''
for x in s:
if x not in alphabet or alphabet.index(x)>=26:
result += x
else:
result += alphabet[alphabet.index(x)+26]
return result
This also handles punctuation such as ; or ..
Update:
As per the OP's request, this is a version without index():
def toUppercase(s):
alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
result = ''
for x in s:
for pos in range(52):
if alphabet[pos] == x:
i = pos
if x not in alphabet or i>=26:
result += x
else:
result += alphabet[i+26]
return result
print(toUppercase('abcdj;shjgh'))
Here is a program to convert the string to uppercase without using inbuilt functions:
Str1=input("Enter the string to be converted uppercase: ")
for i in range (0,len(Str1)):
x=ord(Str1[i])
if x>=97 and x<=122:
x=x-32
y=chr(x)
print(y,end="")
ord()- Return the Unicode code point for a one-character string.
You have to send a one character string as an argument. Here, you are sending the string 'abcd' which has 4 characters which is causing the issue. Send each character separately to the function and thus do 4 calls to the function to get the result.
The below-simplified code help to convert Lower-case alphabets to Upper-case alphabets using a simple calculation
code :
def toUppercase(string):
convertedCharacter = ''
for i in string:
convertCharacter += chr( ( (ord(i)) -32) )
return convertCharacter
char=input("Enter lowercase word :")
for letter in char:
s=ord(letter)
if s>=97 and s<=122:
print(chr(s-32),end=" ")
def uppercase(str_data):
ord('str_data')
str_data = str_data -32
chr('str_data')
return str_data
print(uppercase('abcd'))
in this code ord takes single character as an argument but you have given more than one that's why it's showing an error. Take single character at a time convert it to upper case and make a single string like below.
def convert_to_lower(string):
new=""
for i in string:
j=ord(i)-32 #we are taking the ascii value because the length of lower
#case to uppercase is 32 so we are subtracting 32
if 97<=ord(i)<=122 : #here we are checking whether the charecter is lower
# case or not if lowercase then only we are converting into
#uppercase
new=new+chr(j)
else: #if the character is not the lowercase alplhaber we are taking as it is
new=new+i
print(new)
convert_to_lower("hello world")

How to convert characters of a string from lowercase to upper case and vice versa without using string functions in python?

Write a function which accepts an input string and returns a string
where the case of the characters are changed, i.e. all the uppercase
characters are changed to lower case and all the lower case characters
are changed to upper case. The non-alphabetic characters should not be
changed. Do NOT use the string methods upper(), lower(), or swap().
This is my code:
def changing_cases (input_str):
new_string = []
for i in range(0, len(input_str)):
convert = input_str[i]
value = ord(convert)
if (value >= 65 and value <= 90 ):
value += 32
new_string.append(chr(value))
elif (value >= 97 and value <= 122):
value -= 32
new_string.append(chr(value))
return (str(new_string))
#Main Program
input_str = "Hello"
result = changing_cases (input_str)
print (result)
This code works as expected but there are two major problems with this.
Firstly the output which it returns to the Main is a list, I want it as a string.
Second, how to check whether the string contains special cases and by pass it if there is a special character. Special characters are scattered all over the ASCII table.
Any help would be appreciated.
The string method .join() can help you to unite a list and return a string. But without that knowledge, you could have done this string concatenation.(For that you need to initialize new_string with "", not [])
Join usage:
"".join(["h","e","l","l","o"])
# "hello"
To your second question. You could check if an input is from the alphabet with the .isalpha() method. Which returns a boolean value.
"a".isalpha()
# True
"&".isalpha()
# False
And a suggestion about the solution, You could import the uppercase and lowercase alphabets from the string module. After that, iterating over the term and swapping letters using the alphabet strings is very easy. Your solution is fine for understanding how ascii table works. But with the way I mentioned, you can avoid facing problems about special cases. It is a poor method for cryptology though.
Concerning the first problem. I've found it may be possible to use:
print ','.join(result)
or
print str(result).strip('[]')
Good luck!
def changing_cases (input_str):
new_string = []
for i in range(0, len(input_str)):
convert = input_str[i]
value = ord(convert)
if 65 <= value <= 90:
value += 32
new_string.append(chr(value))
elif 97 <= value <= 122:
value -= 32
new_string.append(chr(value))
else:
return
return ''.join(new_string)
So this function will return None if there are any special characters in string and you simply add if conditon to check if result is None then you just skip this word
You are close:
def changing_cases (input_str):
new_string = []
for i in range(0, len(input_str)):
convert = input_str[i]
value = ord(convert)
if (value >= 65 and value <= 90 ):
value += 32
new_string.append(chr(value))
elif (value >= 97 and value <= 122):
value -= 32
new_string.append(chr(value))
else: #if special character
new_string.append(chr(value))
return (''.join(new_string))
#Main Program
input_str = "Hello"
result = changing_cases (input_str)
print (result)
In python3, one option is to use str.translate.
First, use string methods - string.ascii_uppercase and string.ascii_lowercase to build strings with entire character sets 'A..Z' and 'a..z'. Use str.maketranslate to make a translation table, one for upper case letters to lower case and another for lower case to upper case letters. Finally, loop through the required string, and use str.translate to build the converted string.
import re
import string
test_str = r'The Tree outside is gREEN'
new_str = ''
str_lower = string.ascii_lowercase
#'abcdefghijklmnopqrstuvwxyz'
str_upper = string.ascii_uppercase
#'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
tr_to_upper = str.maketrans(str_lower, str_upper)
tr_to_lower = str.maketrans(str_upper, str_lower)
for char in test_str:
if re.findall(r'[A-Z]', char):
new_str = new_str + char.translate(tr_to_lower)
elif re.findall(r'[a-z]', char):
new_str = new_str + char.translate(tr_to_upper)
else:
new_str = new_str + char
print('{}'.format(new_str))
Output:
tHE tREE OUTSIDE IS Green
How about a cipher? It's not quite as readable but it seriously reduces the line count.
def swap_cases(data):#ONLY INTENDED FOR USE WITH ASCII! EBCDIC OR OTHER INTERCHANGE CODES MAY BE PROBLEMATIC!
output = list(data);
for i in range(0,len(data)):
if ord(output[i]) > 64 < 123: output[i] = chr(ord(data[i]) ^ ord(list(" "*70)[i % len(" "*70)]));
return "".join(output);
print(swap_cases(input("ENTRY::")))
No effecting special characters or anything not in the alphabet, 6 lines of code, relatively fast algorithm no external modules, doesn't use swap() or others string functions and contains only one if block, returning a string as requested not a list.
EDIT:
Come to think of it you can reduce a lot of the clutter by doing this:
if ord(output[i]) > 64 < 123: output[i] = chr(ord(data[i]) ^ 32);
instead of:
if ord(output[i]) > 64 < 123: output[i] = chr(ord(data[i]) ^ ord(list(" "*70)[i % len(" "*70)]));

Modified Vigenere Cipher in python - Alphabet

This is what I have to do:
Write a script in Python that is an implementation of a version of the Vigenere cipher for English text. Your script should distinguish lowercase and uppercase letters (i.e., the encryption key and the plaintext are allowed to be composed of lowercase and uppercase letters but the ciphertext should be uppercase). In addition to letters, there will be four other characters in the plain text: comma (26), dot (27), dash (28), underscore (29) changing the encryption function to be under mod 30.
Your script should read from standard input and write to standard output. It should prompt the user for the encryption key of size k. The key is not allowed to be repeated as in the standard Vigenere cipher. Instead, we will follow a block cipher-based idea. Basically, the plaintext and ciphertext will have blocks of size k which is same as the key size. If the key length is shorter than the plaintext, the ciphertext of block size k of the previous block is concatenated to the key. Here is an example when the keyword is ”Carbondale” with k = 10:
Plaintext : SIU_CS-Department_is_the_best
Key : CarbondaleUIHAQBBDPTUZ,MUOUCX
Ciphertext: UIHAQBBDPTUZ,MUOUCXHTODQTPYUM
So, I want to know how to tackle the part of the extra characters "," "." "/" "_". This is the function that is making the encryption:
a = len(key)
b = len(text)
while (len(key1) <= len(text1)):
for i in range(0,a):
num1 = ord(text1[i+var])%97
num2 = ord(key1[i+var])%97
num3 = num1+num2
if (num3 > 25):
encr.append(num3%25)
else:
encr.append(num3)
i + 1
for i in range(0,a):
encr1.append(chr(encr[i+var]+97))
i + 1
for i in range(0,a):
key1.append(encr1[i+var])
i + 1
var = var + a
You code currently has the following problems (I am assuming that var = 0, encr = [], encr1 = [], key1 = list(key) and text1 = list(text) happen before the code you have posted):
Your while loop will never start if the key is longer than the plaintext, and never end otherwise, as you never shorten text1 (and doing so would break your indexing);
You don't need to manually increment the counter in a for loop (and if you wanted to, i + 1 without assignment effectively does nothing, you need i += 1);
When using mod (%), you don't need to check if e.g. num3 < 25; and
If you get around to including them, note that the extra characters you list aren't the same as those specified ("/" != "-").
Rather than using ord and chr, I would build my own alphabet to cycle over, e.g.
from string import ascii_uppercase
alphabet = list(ascii_uppercase) + [",", ".", "-", "_"]
You might find this implementation of the "standard" Vigenère helpful:
from itertools import cycle
def vigenere(plain, key, alphabet):
for p, k in zip(plain, cycle(key)):
index = alphabet.index(p) + alphabet.index(k)
yield alphabet[index % len(alphabet)]
Here is some pseudo code for your modified implementation:
convert all text (plain, key) to upper case
create a list to hold output
split plain into blocks of length len(key)
for each block in plain:
if it's the first block, use key to encode
otherwise, use the last block of encoded text in output

Categories

Resources