Caesar Cipher in Python - how to replace characters - python-3.x

I'm trying to re-arrange long sentence from a puzzle that is encoded using a Caesar Cipher.
Here is my code.
sentence="g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
import string
a=string.ascii_lowercase[]
b=a[2:]+a[:2]
for i in range(26):
sentence.replace(sentence[sentence.find(a[i])],b[i])
Am I, missing anything in replace function?
When I tried sentence.replace(sentence[sentence.find(a[0])],b[0])
it worked but why I can't loop through?
Thanks.

sentence.replace
returns a new string, which you are immediately throwing away. Note that replacing each character repeatedly will cause duplicate replacements in your cipher. See #RemcoGerlich's answer for a better-detailed explanation of what is wrong. As for the solution, what about
import string
letters = string.ascii_lowercase
shifted = {l: letters[(i + 2) % len(letters)] for i, l in enumerate(letters)}
sentence = ''.join(shifted.get(c, c) for c in sentence.lower())
or if you really want the tabled way:
from string import ascii_lowercase
rotated_lowercase = ascii_lowercase[2:] + ascii_lowercase[:2]
translation_table = str.maketrans(ascii_lowercase, rotated_lowercase)
sentence = sentence.translate(translation_table)

There are a few problems:
One, sentence[sentence.find(a[i])] is strange. It tries to look up where in the sentence the character a[1] occurs, and then looks up which character is there. Well, you already know -- a[1]. Unless that character doesn't occur in the string, then .find will return -1, and sentence[-1] is the last character in the sentence. Probably not what you meant. So instead you meant sentence.replace(a[i], b[i]).
But, you don't save the result anywhere. You meant sentence = sentence.replace(a[i], b[i]).
But that still doesn't work! What if a should be changed into b, and then b into c? Then the original as are also changed into c! That's a fundamental problem with your approach.
Better solutions are given by modesitt. Mine would have been something like
lookupdict = {a_char: b_char for (a_char, b_char) in zip(a, b)}
sentence_translated = [lookupdict.get(s, '') for s in sentence]
sentence = ''.join(sentence_translated)

Related

how to remove word that is split into characters from list of strings

I have a list of sentences, where some of them contain only one word but it is split into characters. How can I either merge the characters to make it one word or drop the whole row?
list = ['What a rollercoaster', 'y i k e s', 'I love democracy']
I try to avoid writing regular expressions as much as I can, but from what you told me, this one could work :
import re
a = ['What a rollercoaster', 'y i k e s', 'I love democracy']
regex = re.compile(r'^(\w ){2,}.')
result = list(filter(regex.search, a))
This captures strings having at least two groups of character and space, followed by anything else. This is assuming you wouldn't have a sentence beginning with something like 'a a foo'.

removing the matched character in string

original_string ="helloworld"
characters_to_remove="world"
for character in characters_to_remove:
if original_string.find(character) == -1:
continue
else:
# remove matched character
original_string = original_string.replace(character,'',1)
print(original_string)
output:hello
(BUt get getting output is:helol) can any one resolve this issue
Your problem is obvious.
The 'l' in 'world' also appears in 'hello'. So what is happening is two things: your code is removing the first l in hello and then ignoring the l in world, because you are actually looping through 'w','o','r','l','d' just once.
A far better way to do this is to use a regex and python's great many string libraries, for example:
import re
re.sub('world', '', "helloworld")
Without the re module you can use find and replace functions to replace the partial string :
original_string ="helloworld"
characters_to_remove="world"
pos = original_string.find(characters_to_remove)
original_string = original_string.replace(original_string[pos:pos + len(characters_to_remove)],"")

How can I replace each letter in the sentence to sentence without breaking it?

Here's my problem.
sentence = "This car is awsome."
and what I want do do is
sentence.replace("a","<emoji:a>")
sentence.replace("b","<emoji:b>")
sentence.replace("c","<emoji:c>")
and so on...
But of course if I do it in that way the letters in "<emoji:>" will also be replaced as I go along. So how can I do it in other way?
As Carlos Gonzalez suggested:
create a mapping dict and apply it to each character in sequence:
sentence = "This car is awsome."
# mapping
up = {"a":"<emoji:a>",
"b":"<emoji:b>",
"c":"<emoji:c>",}
# apply mapping to create a new text (use up[k] if present else default to k)
text = ''.join( (up.get(k,k) for k in sentence) )
print(text)
Output:
This <emoji:c><emoji:a>r is <emoji:a>wsome.
The advantage of the generator expression inside the ''.join( ... generator ...) is that it takes each single character of sentence and either keeps it or replaces it. It only ever touches each char once, so there is no danger of multiple substitutions and it takes only one pass of sentence to convert the whole thing.
Doku: dict.get(key,default) and Why dict.get(key) instead of dict[key]?
If you used
sentence = sentence.replace("a","o")
sentence = sentence.replace("o","k")
you would first make o from a and then make k from any o (or a before) - and you would have to touch each character twice to make it happen.
Using
up = { "a":"o", "o":"k" }
text = ''.join( (up.get(k,k) for k in sentence) )
avoids this.
If you want to replace more then 1 character at a time, it would be easier to do this with regex. Inspired by Passing a function to re.sub in Python
import re
sentence = "This car is awsome."
up = {"is":"Yippi",
"ws":"WhatNot",}
# modified it to create the groups using the dicts key
text2 = re.sub( "("+'|'.join(up)+")", lambda x: up[x.group()], sentence)
print(text2)
Output:
ThYippi car Yippi aWhatNotome.
Doku: re.sub(pattern, repl, string, count=0, flags=0)
You would have to take extra care with your keys, if you wanted to use "regex" specific characters that have another meaning if used as regex-pattern - f.e. .+*?()[]^$

Print First Letter of Each Word in a String in Python (Keep Punctuation Marks)

First post to site so I apologize if I do something wrong. I have looked for an appropriate answer, but could not find one.
I am new to python and have been playing around trying to take a long string (passage in a book,) and printing all but the first letter of each word while keeping the punctuation marks (Though not apostrophe marks.) and have been unsuccessful so far.
Example:
input = "Hello, I'm writing a sentence. (Though not a good one.)"
Code....
output = H, I W A S. (T N A G O.)
--Note the ",", ".", "()", but not the " ' ".
Any tips? Thank you all so much for taking the time to look
To help you on your adventure, I'll give you like a step-by-step logic of it
In python first use the .split() to seperate it by spaces
Go through each string in the list
Go through every char in the string
Print any punctuation marks that you specify and the first alphabetical character you find

Python Challenge # 2 = removing characters from a string

I have the code:
theory = """}#)$[]_+(^_#^][]_)*^*+_!{&$##]((](}}{[!$#_{&{){
*_{^}$#!+]{[^&++*#!]*)]%$!{#^&%(%^*}#^+__])_$#_^#[{{})}$*]#%]{}{][#^!#)_[}{())%)
())&##*[#}+#^}#%!![#&*}^{^(({+#*[!{!}){(!*#!+#[_(*^+*]$]+#+*_##)&)^(#$^]e#][#&)(
%%{})+^$))[{))}&$(^+{&(#%*#&*(^&{}+!}_!^($}!(}_##++$)(%}{!{_]%}$!){%^%%#^%&#([+[
_+%){{}(#_}&{&++!#_)(_+}%_#+]&^)+]_[#]+$!+{#}$^!&)#%#^&+$#[+&+{^{*[#]#!{_*[)(#[[
]*!*}}*_(+&%{&#$&+*_]#+#]!&*#}$%)!})#&)*}#(#}!^(]^#}]#&%)![^!$*)&_]^%{{}(!)_&{_{
+[_*+}]$_[##_^]*^*##{&%})*{&**}}}!_!+{&^)__)#_#$#%{+)^!{}^#[$+^}&(%%)&!+^_^#}^({
*%]&#{]++}#$$)}#]{)!+#[^)!#[%#^!!"""
#theory = open("temp.txt")
key = "##!$%+{}[]_-&*()*^#/"
new2 =""
print()
for letter in theory:
if letter not in key:
new2 += letter
print(new2)
This is a test piece of code to solve the python challenge #2: http://www.pythonchallenge.com/pc/def/ocr.html
The only trouble is, the code I wrote seems to leaves lots of whitespace but I'm not sure why.
Any ideas on how to remove the unnecessary white? In other words I want the code to return "e" not " e ".
The challenge is to find a rare character. You could use collections.Counter for that:
from collections import Counter
c = Counter(theory)
print(c.most_common()[-1])
Output
('e', 1)
The unnecessary whitespace could be removed using .strip():
new2.strip()
Adding '\n' to the key works too.
The best would be to use regular expression library, like so
import re
characters = re.findall("[a-zA-Z]", sourcetext)
print ("".join(characters))
In a resulting string you will have ONLY an alphabetic characters.
If you look at the distribution of characters (using collections.Counter), you get:
6000+ each of )#(]#_%[}!+$&{*^ (which you are correctly excluding from the output)
1220 newlines (which you are not excluding from the output)
1 each of — no, I'm not going to give away the answer
Just add \n to your key variable to exclude the unwanted newlines. This will leave you with just the rare (i.e., 1 occurrence only) characters you need.
P.S., it's highly inefficient to concatenate strings in a loop. Instead of:
new2 =""
for letter in theory:
if letter not in key:
new2 += letter
write:
new2 = ''.join(letter for letter in theory if letter not in key)
The theory string contains several newlines. They get printed by your code. You can either get rid of the newline, like this:
theory = "}#)$[]_+(^_#^][]_)*^*+_!{&$##]((](}}{[!$#_{&{){" \
"*_{^}$#!+]{[^&++*#!]*)]%$!{#^&%(%^*}#^+__])_$#_^#[{{})}$*]#%]{}{][#^!#)_[}{())%)" \
"())&##*[#}+#^}#%!![#&*}^{^(({+#*[!{!}){(!*#!+#[_(*^+*]$]+#+*_##)&)^(#$^]e#][#&)(" \
"%%{})+^$))[{))}&$(^+{&(#%*#&*(^&{}+!}_!^($}!(}_##++$)(%}{!{_]%}$!){%^%%#^%&#([+[" \
"_+%){{}(#_}&{&++!#_)(_+}%_#+]&^)+]_[#]+$!+{#}$^!&)#%#^&+$#[+&+{^{*[#]#!{_*[)(#[[" \
"]*!*}}*_(+&%{&#$&+*_]#+#]!&*#}$%)!})#&)*}#(#}!^(]^#}]#&%)![^!$*)&_]^%{{}(!)_&{_{" \
"+[_*+}]$_[##_^]*^*##{&%})*{&**}}}!_!+{&^)__)#_#$#%{+)^!{}^#[$+^}&(%%)&!+^_^#}^({" \
"*%]&#{]++}#$$)}#]{)!+#[^)!#[%#^!!"
or your can filter them out, like this:
key = "##!$%+{}[]_-&*()*^#/\n"
Both work fine (yes, I tested).
a simpler way to output the answer is to:
print ''.join([ c for c in theory if c not in key])
and in your case you might want to add the newline character to key to also filter it out:
key += "\n"
You'd better work in reverse, something like this:
out = []
for i in theory:
a = ord(i)
if (a > 96 and a < 122) or (a > 65 and a < 90):
out.append(chr(a))
print ''.join(out)
Or better, use a regexp.

Resources