How to keep "spaces" although dict contains it? - python

i am simply trying to read from a file encrypt and display it.
And I want to display the result word by word, but somehow spaces are removed (my dict also contains ' ':' ') and result text is displayed without spaces.
For example,
aa bb cc is what i read from file,
Current Output is ffggğğ, but i want it as ff gg ğğ
...please help...
monocrypt = {
'a': 'f',
'b': 'g',
'c': 'ğ',
'ç': 'h',
'd': 'ı',
'e': 'i',
'f': 'j',
'g': 'k',
'ğ': 'l',
'h': 'm',
'ı': 'n',
'i': 'o',
'j': 'ö',
'k': 'p',
'l': 'r',
'm': 's',
'n': 'ş',
'o': 't',
'ö': 'u',
'p': 'ü',
'r': 'v',
's': 'y',
'ş': 'z',
't': 'a',
'u': 'b',
'ü': 'c',
'v': 'ç',
'y': 'd',
'z': 'e',
' ': ' ',
}
inv_monocrypt = {}
for key, value in monocrypt.items():
inv_monocrypt[value] = key
f = open("C:\\Hobbit.txt","r")
print("Reading file...")
message = f.read()
crypt = ''.join(i for i in message if i.isalnum())
encrypted_message = []
for letter in crypt:
encrypted_message.append(monocrypt[letter.lower()])
print(''.join(encrypted_message))

You're dropping all the whitespace at this step
crypt = ''.join(i for i in message if i.isalnum())
So leave them all in there. Use dict.get with the default argument to preserve letters that aren't keys
crypt = f.read()
encrypted_message = []
for letter in crypt:
encrypted_message.append(monocrypt.get(letter.lower(), letter.lower()) )
If you really just want to preserve spaces (and not punctuation/other whitespace etc)
message = f.read()
crypt = ''.join(i for i in message if i.lower() in monocrypt)
encrypted_message = []
for letter in crypt:
encrypted_message.append(monocrypt[letter.lower()])
You can simplify a little like this
message = f.read().lower()
crypt = ''.join(i for i in message if i in monocrypt)
encrypted_message = [monocrypt[letter] for letter in crypt]

Not sure if it works as intended since I don't have hobbit.txt but I made a small rewrite to make the code a little simpler. It should also solve your problem.
monocrypt = {
'a': 'f',
'b': 'g',
'c': 'ğ',
'ç': 'h',
'd': 'ı',
'e': 'i',
'f': 'j',
'g': 'k',
'ğ': 'l',
'h': 'm',
'ı': 'n',
'i': 'o',
'j': 'ö',
'k': 'p',
'l': 'r',
'm': 's',
'n': 'ş',
'o': 't',
'ö': 'u',
'p': 'ü',
'r': 'v',
's': 'y',
'ş': 'z',
't': 'a',
'u': 'b',
'ü': 'c',
'v': 'ç',
'y': 'd',
'z': 'e',
' ': ' ',
}
with open("hobbit.txt") as hobbitfile:
file_text = hobbitfile.read()
crypted_message = ""
for char in file_text:
char = char.lower()
if char in monocrypt:
crypted_message += monocrypt[char]
else:
#I don't know if you want to add the other chars as well.
#Uncomment the next line if you do.
#crypted_message += str(char)
pass
print(crypted_message)

Check the docs :
http://docs.python.org/2/library/stdtypes.html#str.isalnum
isalnum drops your whitespaces.
use this if you want to preserve the space characters, everything else stays the same :
crypt = ''.join(i for i in message if i.isalnum() or i==' ')
If you want to preserve all whitespaces, do this :
crypt = ''.join(i for i in message if i.isalnum() or i.isspace())

Related

decrypting song with dictionary values

Hi I want to decrypt the song down below, but when I replace some word
and I keep moving forward in the dictionary keys, I lose my replacement.
For example, when 'O' turns to 'A' it is ok but, when the code continues
the dictionary value in 'A' = 'U'.
So I lose the correct replacement, and I get a wrong answer.
decryption_key = {
'O': 'A', 'D': 'B', 'F': 'C', 'I': 'D', 'H': 'E',
'G': 'F', 'L': 'G', 'C': 'H', 'K': 'I', 'Q': 'J',
'B': 'K', 'J': 'L', 'Z': 'M', 'V': 'N', 'S': 'O',
'R': 'P', 'M': 'Q', 'X': 'R', 'E': 'S', 'P': 'T',
'A': 'U', 'Y': 'V', 'W': 'W', 'T': 'X', 'U': 'Y',
'N': 'Z',
}
SONG = """
sc, kg pchxh'e svh pckvl k covl svps
pcop lhpe zh pcxsalc pch vklcp
k okv'p lsvvo is wcop k isv'p wovp ps
k'z lsvvo jkyh zu jkgh
eckvkvl jkbh o ikozsvi, xsjjkvl wkpc pch ikfh
epovikvl sv pch jhilh, k ecsw pch wkvi csw ps gju
wchv pch wsxji lhpe kv zu gofh
k eou, coyh o vkfh iou
coyh o vkfh iou
"""
SONG = SONG.upper()
for word, value in decryption_key.items():
if word in SONG:
SONG = SONG.replace(word, value)
else:
continue
print(SONG)
One workaround could be:
SONG = SONG.replace(word, value.lower())
This way, letters that are replaced will be lowercased and won't be affected later.
But a proper way would be constructing a new string:
SONG = ''.join(decryption_key.get(c, c) for c in SONG)
This just replaces your whole loop.
decryption_key.get(c, c) part might be confusing, but what it does is it returns the value by the given key if it exists in decryption_key or returns the key itself (as it was specified as a default by the second parameter) otherwise.

function that creates a new words by given pattern

im trying to create a function that takes a word (capital letters and lowercase letters) and map each character to a new character. the pattern is each vowel (AEIOU) becomes the next vowel in order (A -> E, E - > I). For constant letters becomes the thirds letter (B -> F, C -> G)
>>>'hello'
'lippu'
>>> 'today'
'xuhec'
>>> 'yesterday'
'ciwxivhec'
I know that i would have to create two lists:
vowels = ['a', 'e', 'i', 'o', 'u']
constants = ['b', 'c','d','f','g','h','j','k','l','m','n','p', 'q','r', 's','t','v','w','x','y', 'z']
and uses the index() function, to check the current index and add 3 to it, but im stuck after that.
Letters loop back around for cases that extend beyond the list. (x-z, and u)
To compute the maps you can use enumerate (to get the index of the current) together with modulo (for the indices greater than the list length), something like this:
vowels = ['a', 'e', 'i', 'o', 'u']
consonants = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z']
vowels_map = {k: vowels[(i + 1) % len(vowels)] for i, k in enumerate(vowels)}
consonants_map = {k: consonants[(i + 3) % len(consonants)] for i, k in enumerate(consonants)}
print(vowels_map)
print(consonants_map)
Output
{'u': 'a', 'a': 'e', 'o': 'u', 'e': 'i', 'i': 'o'}
{'s': 'w', 'z': 'd', 'v': 'y', 'm': 'q', 'f': 'j', 'h': 'l', 'd': 'h', 'g': 'k', 'q': 't', 'n': 'r', 'p': 's', 'k': 'n', 't': 'x', 'y': 'c', 'r': 'v', 'w': 'z', 'x': 'b', 'l': 'p', 'b': 'f', 'j': 'm', 'c': 'g'}
Note that dictionaries have no order, that being said you could use them in the following way:
def replace_from_dict(word, table):
return ''.join(table[c] for c in word)
words = ['hello',
'today',
'yesterday']
for word in words:
print(replace_from_dict(word, { **vowels_map, **consonants_map }))
Output (from using replace_from_dict)
lippu
xuhec
ciwxivhec
We can use itertools.cycle. First check which category i belongs to either vowel or consonants (not constant). Then create a cycle from that respective list, use while and next until we are at the corresponding letter. If its a vowel we simply append the next value, if its a consonant we advance 2 positions and then append the next value. After use .join() to convert back to string.
from itertools import cycle
vwl = ['a', 'e', 'i', 'o', 'u']
cnst = ['b', 'c','d','f','g','h','j','k','l','m','n','p', 'q','r', 's','t','v','w','x','y', 'z']
s = 'hello'
new = []
for i in s.lower():
if i in vwl:
a = cycle(vwl)
while i != next(a):
next(a)
new.append(next(a))
if i in cnst:
b = cycle(cnst)
while i != next(b):
next(b)
for x in range(2):
next(b)
new.append(next(b))
res = ''.join(new)
print(res)
# lippu
Works for words that include edge letters, zumba produces daqfe
I have defined two dictionaries for edge cases, vowel_dictionary, and a dictionary for letters x/y/z to achieve wrapping. I iterated through the string and if the character is a special character, I use the appropriate dictionary to find the word. However if the character is below 'w' and not a vowel, I just add 4 to its ord value (ASCII value) and convert it char.
def transform(input):
return_string = ""
vowel_dictionary = {
'a': 'e',
'e': 'i',
'i': 'o',
'o': 'u',
'u': 'a'
}
edge_dictionary = {
'x': 'b',
'y': 'c',
'z': 'd'
}
for character in input.lower():
if character in vowel_dictionary:
return_string += vowel_dictionary[character]
elif ord(character) <= ord("v"):
return_string += chr(ord(character) + 4)
else :
return_string += edge_dictionary[character]
return return_string
I have ran a few tests with the code above:
Tests
transform("hello") # => lippu
transform("today") # => xuhec
transform("yesterday") # => ciwxivhec

Decoding message using dictionary on string in Python

When I'm using a dictionary to replace values in a string for decoding of a message, how do I put it in such that the function doesn't read a replaced value as a key and replace a replaced value again?
def decipher_message(translation_guide, message):
t = read_translation_guide_into_dictionary(translation_guide)
e = read_message(message)
print(t)
print(e)
for key, value in t.items():
f = e
e = f.replace(key, value)
return e
Output:
{'a': 'm', 'b': 'a', 'c': 'c', 'd': 'y', 'e': 't', 'f': 'v', 'g': 'o',
'h': 'u', 'i': 'x', 'j': 'e', 'k': 'j', 'l': 'w', 'm': 'f', 'n': 'z',
'o': 'd', 'p': 'l', 'q': 'i', 'r': 'k', 's': 'h', 't': 'n', 'u': 'g',
'v': 'b', 'w': 'q', 'x': 's', 'y': 'p', 'z': 'r'}
"qa mqtbppd vjqtu mghto! esbtr dgh mgz mqtoqtu aj gs rqto xezbtujz. q
ahxe ejpp dgh, qex whqej vgzqtu sbfqtu bpp esj ljbpes qt esj lgzpo. qm
dghzj pggrqtu mgz qe, q vhzqjo b aby eg qe bzghto lsjzj dghzj xebtoqtu
tgl! zjajavjz esghus: ljbpes qxte jfjzdesqtu qt esj lgzpo!"
'"if finallp being fdgnd! nhank pdg fdr finding fe dh kind snranger. i
fgsn nell pdg, ins qgine bdring habing all nhe qealnh in nhe qdrld. if
pdgre lddking fdr in, i bgried a fap nd in ardgnd qhere pdgre snanding
ndq! refefber nhdggh: qealnh isnn eberpnhing in nhe qdrld!"'
Rather than iterating through the dictionary and running a replace against the whole string, you should iterate through the string and replace each character with its value in the dict:
decoded = []
for letter in e:
decoded.append(t.get(letter, letter))
return ''.join(decoded)
Note also that Python has a built-in string translate method, which takes a table which can be generated from your dict:
table = str.maketrans(t)
return e.translate(table)
You can use str.join with the following generator expression that iterates over the string to translate each character:
def decipher(translation, message):
t = read_translation(translation)
e = read_message(message)
return ''.join(t.get(c, c) for c in e)

Python Dictionary file not translating into array

I'm loading a file with words and definitions each on their own line separated by colons. Here is a sample.
Dissaray:A state of confusion and disorderliness
Staid:Steady and serious
Contemptible:Unworthy, wretched, mean
Intertwine:To connect or associate two things
Unwarranted:Not based on truth or valid circumstances
Punctuate:To specifically point out
Validate:To state the soundness or truth of something
Conducive:To contribute in a useful way
I'm trying to read the file with this code:
print("Currently loading file for your level")
dictionary = {}
with open("level"+str(level)+".txt","r+") as f:
for line in f:
print line
line.split(":")
dictionary[line[0]] = line[1]
print("Dictionary file has been loaded")
print(dictionary)
The lines print correctly, but when I print the dictionary array, I get this.
{'A': 'p', 'C': 'o', 'B': 'e', 'E': 'm', 'D': 'e', 'G': 'r', 'F': 'i', 'I': 'r', 'H': 'i', 'J': 'o', 'M': 'i', 'L': 'u', 'O': 'p', 'N': 'o', 'P': 'a', 'S': 'u', 'R': 'e', 'U': 'n', 'T': 'r', 'W': 'a', 'V': 'e', 'Z': 'e'}
I'm not sure what's happening here, can someone please help?
str.split doesn't work in place. You need to assign it to line, otherwise, you'll be indexing the original line:
for line in f:
line = line.split(":")
dictionary[line[0]] = line[1]
More so, you could also simply do:
dictionary = dict(line.split(':') for line in f)
Building the dictionary from a generator expression.
The problem is that .split() returns its value rather than modifying its argument. Actually it has to do that, because strings are immutable in Python. So you just need to change:
line.split(":")
dictionary[line[0]] = line[1]
to
line_parts = line.split(":")
dictionary[line_parts[0]] = line_parts[1]

'str' object is not callable in caesar encryptor

def encrypt_caesar(plaintext):
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
d = dict(map(s,s[3:]+ s[:3]))
return ''.join(map(lambda l: d.get(l,l), plaintext.lower()))
Tried to copy this code from Stanford lecture slide and run it but it gives 'str' object is not callable error, what am I doing wrong?
I guess the author of the slide didn't actually test the code.
The code you produced is trying to use map() to call s as a function:
map(s,s[3:]+ s[:3])
# ^ This must be a *callable object*, like a function
If you wanted to create a dictionary mapping ASCII letters to a letter 3 spots along in the alphabet, use the zip() function instead:
d = dict(zip(s, s[3:] + s[:3]))
zip() then pairs up each element in s with each element in s[3:] + s[:3], and those (letter, letter + 3) pairs are then passed to dict() to form key-value pairs:
>>> s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>>> dict(zip(s, s[3:] + s[:3]))
{'A': 'D', 'I': 'L', 'L': 'O', 'X': 'A', 'N': 'Q', 'D': 'G', 'S': 'V', 'B': 'E', 'V': 'Y', 'C': 'F', 'K': 'N', 'W': 'Z', 'R': 'U', 'O': 'R', 'T': 'W', 'P': 'S', 'F': 'I', 'J': 'M', 'M': 'P', 'E': 'H', 'Q': 'T', 'H': 'K', 'G': 'J', 'Z': 'C', 'U': 'X', 'Y': 'B'}
Next, your last line will completely fail to do any encryption, because your map only handles uppercase letters, but you lowercased your input. Either produce a lowercase map, or lowercase your input.
Lowercasing the map could look like this:
def encrypt_caesar(plaintext):
s = "abcdefghijklmnopqrstuvwxyz"
d = dict(zip(s, s[3:] + s[:3]))
return ''.join(map(lambda l: d.get(l, l), plaintext.lower()))
or you could just use the string.ascii_lowercase constant:
from string import ascii_lowercase
def encrypt_caesar(plaintext):
d = dict(zip(ascii_lowercase, ascii_lowercase[3:] + ascii_lowercase[:3]))
return ''.join(map(lambda l: d.get(l,l), plaintext.lower()))
Using this method is rather slow, however. For blazingly-fast 'encryption', use the str.translate() method; the input map for that is best produced with str.maketrans:
from string import ascii_lowercase as alc
def encrypt_caesar(plaintext, shift=3):
map = str.maketrans(alc, alc[shift:] + alc[:shift])
return plaintext.lower().translate(map)
I added a shift parameter to define how much of an alphabet shift should be applied.
I'll leave handling both lowercase and uppercase letters as an exercise to the reader!

Categories