I had a coded text file that was coded by simple letter shifting. I have now gotten the information from it into two lists. In this format:
list_1 =['fjsir', 'vnjk', 'eioafnvjf', 'einbvfbj']
list_2 =[3,4,7,1]
The second list is by how many places in the alphabet should it move over. For eg. if index 0 in list one had 'fjsir' and it's corresponding index in list_2 is 3 then it would decode to 'cfpeo'. I'm not sure how I would match these in python.
To shift elements to the left:
chr(ord(char) - n)
Which uses ord() to get an integer representation of char, and substracts n from this number. It then uses chr() to convert it back to a character.
The function could look like this:
def shift(s, n):
return ''.join(chr(ord(char) - n) for char in s)
Which can get called nicely with zip():
list_1 =['fjsir', 'vnjk', 'eioafnvjf', 'einbvfbj']
list_2 =[3,4,7,1]
for x, y in zip(list_1, list_2):
print(shift(x, y))
And gives back the decodings:
cgpfo
rjfg
^bhZ_goc_
dhmaueai
Additionally, if you only want decodings to only consist of letters from the English alphabet, you can use a modulus function % instead:
def shift(s, n):
return ''.join(chr((ord(char) - 97 - n) % 26 + 97) for char in s)
Which gives back decodings with only letters:
cgpfo
rjfg
xbhtygocy
dhmaueai
Here is a simple solution:
alphabets = 'abcdefghijklmnopqrstuvwxyz'
list_1 = ['fjsir', 'vnjk', 'eioafnvjf', 'einbvfbj']
list_2 = [3,4,7,1]
final_list = []
for index,original_word in enumerate(list_1):
new_word = ''
for letter in original_word:
if letter in alphabets:
index_val = alphabets.index(letter) - list_2[index]
new_word += alphabets[index_val]
final_list.append(new_word)
print final_list
Output:
['cgpfo', 'rjfg', 'xbhtygocy', 'dhmaueai']
You can convert characters to number with ord() and convert numbers to characters with chr(). A shift function may look like:
def shift_string(inString, inOffset):
return "".join([chr(ord(x) + inOffset) for x in inString])
Accessing array elements from list is left to the reader. ;)
Note: This simple version will probably produce unwanted characters. You may simply use a modulo function to remain in the ASCII range.
def shift_string(inString, inOffset):
return "".join([chr(((ord(x) - ord(' ') + inOffset) % 95) + ord(' ')) for x in inString])
If your set of valid characters is more complex (e.g. letters), you should write a special function for the character transformation.
Related
I'm trying to change numbers in a string to an assigned letter (0 would be 'A', 1 => 'B' and so on...).
In this case I must use a function (def) to change the numbers to the assigned letter. I tried doing the program with if's in a for cycle with indices and it worked, but apperantly there is an easier and shorter solution. I tried doing it with isdigit() instead, but my function doesn't recognize the numbers in the given word and just prints out the same word
You can use a dictionary to map digits to letters, write a generator to get those letters and join it all into a resultant string. The dictionary's get method either gets the value or returns a default. Use this to return any non-digit characters.
>>> digit_map = dict(zip('123456789', 'ABCDEFGHI'))
>>> test = 'a1b2c3'
>>> "".join(digit_map.get(c,c) for c in test)
'aAbBcC'
Use ord to get the numeric value of a character, and chr to turn the numeric value back into a character. That lets you convert a number like 1 into its offset from another character.
Then use join to put it all together:
>>> ''.join(chr(int(c)+ord('A')) if c.isdecimal() else c for c in 'a1b2c3')
'aBbCcD'
One solution would be creating an array that would store each letter as a string as such:
alphabet = ["A", "B", "C"] // and so on
Then you could loop trough the string and find all numbers, then for each numbers get the corresponding letter by accessing it from the array as such py alphabet[i] and feed it back to the string and return that
Here is a simple function that you can use to convert numbers to letters in a given string:
def convert_numbers_to_letters(s):
# Create a dictionary that maps numbers to letters
number_to_letter = {str(i): chr(i + ord('A')) for i in range(10)}
# Iterate through each character in the string
result = ""
for c in s:
# If the character is a number, convert it to a letter
if c.isdigit():
result += number_to_letter[c]
# Otherwise, just add the character to the result
else:
result += c
return result
I have the code below to replace all punctuation with 999 and all alphabet characters with its number position. I have included the print statement that confirms punctuation is being replaced. However I seem to override with my remaining code to replace the other characters.
import string
def encode(text):
punct = '''!()-[]{};:'"\,<>./?##$%^&*_~'''
for x in text.lower():
if x in punct:
text = text.replace(x, ".999")
print(text)
nums = [str(ord(x) - 96)
for x in text.lower()
if x >= 'a' and x <= 'z'
]
return ".".join(nums)
print(encode(str(input("Enter Text: "))))
Input: 'Morning! \n'
Output: '13.15.18.14.9.14.7 \n'
Expected Output: 13.15.18.14.9.14.7.999
No, you have two independent logical "stories" here. One replaces punctuation with 999. The other filters out all the letters and builds an independent list of their alphabetic positions.
nums = [str(ord(x) - 96)
for x in text.lower()
if x >= 'a' and x <= 'z'
]
return ".".join(nums)
Note that this does nothing to alter text, and it takes nothing but letters from text. If you want to include the numbers, do so:
nums = [str(ord(x) - 96)
if x >= 'a' and x <= 'z'
else x
for x in text.lower()
]
return ".".join(nums)
Output of print(encode("[hello]")):
..9.9.9.8.5.12.12.15...9.9.9
nums = [str(ord(x) - 96)
for x in text.lower()
if x >= 'a' and x <= 'z'
]
This means: take every character from the lowercase version of the string, and only if it is between 'a' and 'z', convert the value and put the result in nums.
In the first step, you replace a bunch of punctuation with text that includes '.' and '9' characters. But neither '9' nor '.' is between 'a' and 'z', so of course neither is preserved in the second step.
Now that I understand what you are going for: you have fundamentally the wrong approach to splitting up the problem. You want to separate the two halves of the rule for "encoding" a given part of the input. But what you want to do is separate the whole rule for encoding a single element, from the process of applying a single-element rule to the whole input. After all - that is what list comprehensions do.
This is the concept of separation of concerns. The two business rules are part of the same concern - because implementing one rule doesn't help you implement the other. Being able to encode one input character, though, does help you encode the whole string, because there is a tool for that exact job.
We can have a complicated rule for single characters - no problem. Just put it in a separate function, so that we can give it a meaningful name and keep things simple to understand. Conceptually, our individual-character encoding is a numeric value, so we will consistently encode as a number, and then let the string-encoding process do the conversion.
def encode_char(c):
if c in '''!()-[]{};:'"\,<>./?##$%^&*_~''':
return 999
if 'a' <= c.lower() <= 'z':
return ord(c) - 96
# You should think about what to do in other cases!
# In particular, you don't want digit symbols 1 through 9 to be
# confused with letters A through I.
# So I leave the rest up to you, depending on your requirements.
Now we can apply the overall encoding process: we want a string that puts '.' in between the string representations of the values. That's straightforward:
def encode(text):
return '.'.join(str(encode_char(c)) for c in text)
I'm having trouble with this code below.
My task is to create a function, that among the given numbers finds one that is different in evenness, and returns a position of that number. The numbers are given as a string. So far I have managed to convert the string into an integer list, then using for loop to iterate through each number.
The problem I'm encountering is that I've managed to return only the position of an odd number among the even numbers, and I can't continue on with the code for vice versa action, because it only returns the position of the odd number.
Here is the code:
def iq_test(numbers):
# Splitting the "numbers" string
num_split = numbers.split()
# converting the splitted strings into int
num_map = map(int, num_split)
# converting the object into list
list_num = list(num_map)
for n in list_num:
if not n%2 == 0:
return list_num.index(n) + 1
Your problem is, that you are assuming, that you are searching for the first even number. What you have to do, is to first decide, what you are searching for. You could for example simply first count the number of even numbers. If it is one, then you are looking for an even number, otherwise, you are looking for an odd. As you don't care for the actual numbers, I would map all of them to their value mod 2 as so:
num_map = list(map(lambda x: int(x) % 2, num_split))
Then, the rest is simple. For example like this:
def iq_test(numbers):
# Splitting the "numbers" string
num_split = numbers.split()
# converting the splitted strings into even (0) or odd (1)
num_map = list(map(lambda x: int(x) % 2, num_split))
# return the correct position based on if even or odd is in search
evens = num_map.count(0)
if evens == 1:
return num_map.index(0) + 1
else:
return num_map.index(1) + 1
I came up with a similar and a little bit shorter solution
def iq_test(numbers):
# first check what im looking for "even" or "odd", map a lambda function that basically does it for me, using the numbers argument as a list type and afterwards cast it into a list so i can iterate later on
num_map = list(map(lambda x: 'e' if int(x) % 2 == 0 else 'o', numbers.split()))
# search for even numbers numbers
if num_map.count('e') == 1:
return num_map.index('e') + 1
# search for odd numbers numbers
return num_map.index('o') + 1
def iq_test(numbers):
# Splitting the "numbers" string
num_split = numbers.split()
# converting the splitted strings into int
num_map = map(int, num_split)
# converting the object into list
list_num = list(num_map)
for n in list_num:
if not n%2 == 0:
return list_num.index(n) + 1
I am trying to take a value from user and read a list of words from a Passwords.txt file, and shift each letter to right by value
•def shift():
value=eval(input("Please enter the value here."))
file = open("Passwords.txt","w")
with open ("word-text.txt","r") as m:
for line in m:
line=line.strip()
print (line)
newString = ""
for char in line:
char_int=ord(char)
t=char_int+value
if t==124:
t = t-27
charme= chr(t)
print (char,">>",charme)
newString += charme
file.writelines(line+" "+newString+"\n")
you don't have to convert to ascii, you can just use maketrans function
def shift_string(text, shift):
intab='abcdefghijklmnopqrstuvwxyz'
outab=intab[shift:]+intab[:shift]
return maketrans(intab, outab)
You need to do the assignment yourself (or there is no point in learning to program) and if you don't understand the question, you should ask your teacher for clarification.
That said, shifting is quite simple in principle. You can do it by hand. If you have a letter, say A, shifting it by 1 (key = 1) would transform it into B. In the assignment you shift by 2 places, so A would become C, B (in the original word) would be become D and so on. You have to be a bit careful about the end of the alphabet. When shifting by 1, Z becomes A. When shifting by 2, Y becomes A and Z becomes B.
So in your example, HELLO becomes JGNNQ because when shifting 2 places:
H => J
E => G
L => N
O => Q
(Note: I'm using uppercase for readability but your assignment seems to be about working on lowercase characters. I'm assuming you're only asked to handle lowercase.)
How do you do this? Check out the links you were given. Basically ord() transforms a character into an integer and chr() transforms one such integer into a character. It's based on the way characters are represented as numbers in the computer. So for a given character, if you transform it into its ord(), you can add the key to shift it and then transform it back into a character with chr().
For wrapping from Y and Z to A and B, you can use the modulus operator (%) for this but be careful, it's a bit fiddly (you need to calculate the difference between the ord of your character and the ord of 'a', apply % 26 (which gives you a number between 0 and 25), then add it to ord('a) to have the correct ord). If it's too complicated, just do it with a couple of IFs.
I'd advise to start with a small program that takes input from the user and prints the output to check that it's working correctly. You won't need the input and print in the final version but it will help you to test that your shifting code works correctly.
Then you have the part about reading from a file and writing to a file. Your assignment doesn't ask the user for input, instead it reads from a file. Your line with open ("word-text.txt","r") as f: looks fine, this should give you the file handle you need to read the data. You can read the data with f.read() and assign it to a variable. I'm not sure what you've been taught, but I'd split the string into words with <string>.split() which creates a list of strings (your words).
Then for each word, you use the code you wrote previously to shift the string and you can just write both the original word and the shifted word into the output file. The simplest would probably be to start by opening the output file (in writing mode) and do both the shifting and the writing in one go by looping on the list.
The heavy lifting is doing the word conversion, so I've done that - you can do the rest as it's very trivial. :)
This works by converting each character into a numeric representation and correcting for circular performance (i.e. Z shifted by 2 will output B).
def limits_correction(character, distance, start, end):
char = character
if char >= start and char < end:
if char + distance >= end:
char = char + distance - 26
else:
char = char + distance
return char
def modify_string(string, distance):
ords = [ord(c) for c in string]
corrected_distance = 0
if distance > 26:
corrected_distance = distance % 26
elif distance > 0 and distance <= 26:
corrected_distance = distance
lower_start = 97
lower_end = lower_start + 26
upper_start = 65
upper_end = upper_start + 26
shifted_string = []
for char in ords:
if char >= lower_start and char < lower_end:
char = limits_correction(char, corrected_distance, lower_start, lower_end)
elif char >= upper_start and char < upper_end:
char = limits_correction(char, corrected_distance, upper_start, upper_end)
shifted_string.append(chr(char))
return ''.join(shifted_string)
This also works for uppercase and lowercase for any integer shift number (read as from 0 to very large).
REFERENCE:
http://www.asciitable.com/
This question already has answers here:
how to find words that made up of letter exactly facing each other? (python) [closed]
(4 answers)
Closed 9 years ago.
I have to write a function which takes one arguments text containing a block of text in the form of a str, and returns a sorted list of “symmetric” words. A symmetric word is defined as a word where for all values i, the letter i positions from the start of the word and the letter i positions from the end of the word are equi-distant from the respective ends of the alphabet. For example, bevy is a symmetric word as: b (1 position from the start of the word) is the second letter of the alphabet and y (1 position from the end of the word) is the second-last letter of the alphabet; and e (2 positions from the start of the word) is the fifth letter of the alphabet and v (2 positions from the end of the word) is the fifth-last letter of the alphabet.
For example:
>>> symmetrics("boy bread aloz bray")
['aloz','boy']
>>> symmetrics("There is a car and a book;")
['a']
All I can think about the solution is this but I can't run it since it's wrong:
def symmetrics(text):
func_char= ",.?!:'\/"
for letter in text:
if letter in func_char:
text = text.replace(letter, ' ')
alpha1 = 'abcdefghijklmnopqrstuvwxyz'
alpha2 = 'zyxwvutsrqponmlkjihgfedcba'
sym = []
for word in text.lower().split():
n = range(0,len(word))
if word[n] == word[len(word)-1-n]:
sym.append(word)
return sym
The code above doesn't take into account the position of alpha1 and alpha2 as I don't know how to put it. Is there anyone can help me?
Here is a hint:
In [16]: alpha1.index('b')
Out[16]: 1
In [17]: alpha2.index('y')
Out[17]: 1
An alternative way to approach the problem is by using the str.translate() method:
import string
def is_sym(word):
alpha1 = 'abcdefghijklmnopqrstuvwxyz'
alpha2 = 'zyxwvutsrqponmlkjihgfedcba'
tr = string.maketrans(alpha1, alpha2)
n = len(word) // 2
return word[:n] == word[::-1][:n].translate(tr)
print(is_sym('aloz'))
print(is_sym('boy'))
print(is_sym('bread'))
(The building of the translation table can be easily factored out.)
The for loop could be modified as:
for word in text.lower().split():
for n in range(0,len(word)//2):
if alpha1.index(word[n]) != alpha2.index(word[len(word)-1-n]):
break
else:
sym.append(word)
return sym
According to your symmetric rule, we may verify a symmetric word with the following is_symmetric_word function:
def is_symmetric_word(word):
alpha1 = 'abcdefghijklmnopqrstuvwxyz'
alpha2 = 'zyxwvutsrqponmlkjihgfedcba'
length = len(word)
for i in range(length / 2):
if alpha1.index(word[i]) != alpha2.index(word[length - 1 - i]):
return False
return True
And then the whole function to get all unique symmetric words out of a text can be defined as:
def is_symmetrics(text):
func_char= ",.?!:'\/;"
for letter in text:
if letter in func_char:
text = text.replace(letter, ' ')
sym = []
for word in text.lower().split():
if is_symmetric_word(word) and not (word in sym):
sym.append(word)
return sym
The following are two test cases from you:
is_symmetrics("boy bread aloz bray") #['boy', 'aloz']
is_symmetrics("There is a car and a book;") #['a']
Code first. Discussion below the code.
import string
# get alphabet and reversed alphabet
try:
# Python 2.x
alpha1 = string.lowercase
except AttributeError:
# Python 3.x and newer
alpha1 = string.ascii_lowercase
alpha2 = alpha1[::-1] # use slicing to reverse alpha1
# make a dictionary where the key, value pairs are symmetric
# for example symd['a'] == 'z', symd['b'] == 'y', and so on
_symd = dict(zip(alpha1, alpha2))
def is_symmetric_word(word):
if not word:
return False # zero-length word is not symmetric
i1 = 0
i2 = len(word) - 1
while True:
if i1 >= i2:
return True # we have checked the whole string
# get a pair of chars
c1 = word[i1]
c2 = word[i2]
if _symd[c1] != c2:
return False # the pair wasn't symmetric
i1 += 1
i2 -= 1
# note, added a space to list of chars to filter to a space
_filter_to_space = ",.?!:'\/ "
def _filter_ch(ch):
if ch in _filter_to_space:
return ' ' # return a space
elif ch in alpha1:
return ch # it's an alphabet letter so return it
else:
# It's something we don't want. Return empty string.
return ''
def clean(text):
return ''.join(_filter_ch(ch) for ch in text.lower())
def symmetrics(text):
# filter text: keep only chars in the alphabet or spaces
for word in clean(text).split():
if is_symmetric_word(word):
# use of yield makes this a generator.
yield word
lst = list(symmetrics("The boy...is a yob."))
print(lst) # prints: ['boy', 'a', 'yob']
No need to type the alphabet twice; we can reverse the first one.
We can make a dictionary that pairs each letter with its symmetric letter. This will make it very easy to test whether any given pair of letters is a symmetric pair. The function zip() makes pairs from two sequences; they need to be the same length, but since we are using a string and a reversed copy of the string, they will be the same length.
It's best to write a simple function that does one thing, so we write a function that does nothing but check if a string is symmetric. If you give it a zero-length string it returns False, otherwise it sets i1 to the first character in the string and i2 to the last. It compares characters as long as they continue to be symmetric, and increments i1 while decrementing i2. If the two meet or pass each other, we know we have seen the whole string and it must be symmetric, in which case we return True; if it ever finds any pair of characters that are not symmetric, it returns False. We have to do the check for whether i1 and i2 have met or passed at the top of the loop, so it won't try to check if a character is its own symmetric character. (A character can't be both 'a' and 'z' at the same time, so a character is never its own symmetric character!)
Now we write a wrapper that filters out the junk, splits the string into words, and tests each word. Not only does it convert the chosen punctuation characters to spaces, but it also strips out any unexpected characters (anything not an approved punctuation char, a space, or a letter). That way we know nothing unexpected will get through to the inner function. The wrapper is "lazy"... it is a generator that yields up one word at a time, instead of building the whole list and returning that. It's easy to use list() to force the generator's results into a list. If you want, you can easily modify this function to just build a list and return it.
If you have any questions about this, just ask.
EDIT: The original version of the code didn't do the right thing with the punctuation characters; this version does. Also, as #heltonbiker suggested, why type the alphabet when Python has a copy of it you can use? So I made that change too.
EDIT: #heltonbiker's change introduced a dependency on Python version! I left it in with a suitable try:/except block to handle the problem. It appears that Python 3.x has improved the name of the lowercase ASCII alphabet to string.ascii_lowercase instead of plain string.lowercase.