Python - Read two letters in table from string - python

I'm doing a ADFGVX cipher encoder and decoder and I have a polybius square where I need to read two letters.
reversesquare = {'AA': '1', 'AD': '2', 'AF': '3', 'AG': '4', 'AV': '5', 'AX': '6',
'DA': '7', 'DD': '8', 'DF': '9', 'DG': '0', 'DV': 'Q', 'DX': 'W',
'FA': 'E', 'FD': 'R', 'FF': 'T', 'FG': 'Y', 'FV': 'U', 'FX': 'I',
'GA': 'O', 'GD': 'P', 'GF': 'A', 'GG': 'S', 'GV': 'D', 'GX': 'F',
'VA': 'G', 'VD': 'H', 'VF': 'J', 'VG': 'K', 'VV': 'L', 'VX': 'Z',
'XA': 'X', 'XD': 'C', 'XF': 'V', 'XG': 'B', 'XV': 'N', 'XX': 'M'}
def Decrypt_Final(sortedcipher):
mensagemcifrada = ""
for letter in sortedcipher: # Esta a ler letra a letra quando devia ser 2 de uma vez
if letter in reversesquare:
mensagemcifrada += (reversesquare[letter])
return mensagemcifrada
I have this function which is gonna take a string (sortedcipher) like: GXFXVVFXGDFA
I want my program to read two letters each time its loops over it like, "GX" "FX" .... and find in my reversesquare when it matches.

A "one-liner", though less readable, would be following.
def Decrypt_Final(sortedcipher):
return "".join([
reversesquare[sortedcipher[i:i+2]]
for i in range(0, len(sortedcipher), 2)
]
)

You could zip the string with an offset like this:
for a, b in zip(sortedcipher, sortedcipher[1:]):
pair = a + b
# pair now contains "GF", "FX" and so on..

Another option is to use list-comprehensoion:
def Decrypt_Final(sortedcipher):
# turns the cipher into a list of strings where each element
# contains two characters from the string
letters = (sortedcipher[i:i+2] for i in range(0, len(sortedcipher), 2))
return [reversesquare[letter] for letter in letters]

Related

Is there any downside to iterating over a range for more times than is required to generate desired output?

I'm taking a 100 Days of Code in Python, and I'm trying to create a Python password generator by taking in user input for how many letters, numbers, and symbols they'd like in their password.
The program below runs and generates the desired output, but I know there must be a better way than iterating over the range an arbitrary number of times to generate a fixed-length password.
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']
print("Welcome to the PyPassword Generator!")
nr_letters= int(input('How many letters would you like in your password?: '))
nr_symbols = int(input('How many symbols would you like?: '))
nr_numbers = int(input('How many numbers would you like?: '))
password = ""
# Generate unshuffled password
# for i in range(1, (nr_letters + 1)):
# password += random.choice(letters)
# for i in range(1, (nr_symbols + 1)):
# password += random.choice(numbers)
# for i in range(1, (nr_numbers +1)):
# password += random.choice(symbols)
# print(password)
letter_counter = 0
symbol_counter = 0
number_counter = 0
# NOTE: This seems dumb but it works so...
for i in range(0, 100):
random_int = random.randint(0, 2)
if random_int == 0 and letter_counter < nr_letters:
password += random.choice(letters)
letter_counter += 1
elif random_int == 1 and symbol_counter < nr_symbols:
password += random.choice(symbols)
symbol_counter += 1
elif random_int == 2 and number_counter < nr_numbers:
password += random.choice(numbers)
number_counter += 1
print(password)
Is there a cleaner way I can create a shuffled, fixed-length string through a Python for loop?
For the future, is there a major downside to iterating through a loop more times than it takes to generate the desired output?
I think this is what you are looking for?:
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']
print("Welcome to the PyPassword Generator!")
nr_letters= int(input('How many letters would you like in your password?: '))
nr_symbols = int(input('How many symbols would you like?: '))
nr_numbers = int(input('How many numbers would you like?: '))
password = ""
for i in range(1, sum([nr_letters,nr_numbers,nr_symbols])+1):
if i<= nr_letters:
password += random.choice(letters)
elif i > nr_letters and i<=nr_symbols+nr_letters:
password += random.choice(symbols)
elif i > nr_symbols+nr_letters:
password += random.choice(numbers)
password=[x for x in password]
random.shuffle(password)
password="".join(password)
print(password)
Basically, instead of iterating through an arbitrary number, it adds up the number of characters required, then it goes through the range, and adding the numbers/symbols/letters when the requirements are met.
The requirements are basic. While below or equal to the number of letters required, add a letter. Then, you go and add the number of letters and symbols together, and say that it has to meet both above the number of letters, and below letters+symbols. Then, if i is above letters+symbols, add numbers. That's pretty much the pattern.
The downside is that you will have to shuffle afterwards like I've done above. password=[x for x in password] basically turns password into a list. Then you shuffle it with random.shuffle(password), and then join it password="".join(password).
It's basically your first commented out iterations bundled up in 1.

How to get my Python Morse code-decoder to separate after a word has been translated?

When I get my decoder to run I can translate a word from Morse to normal but if I use more than one word it doesn't separate the words, how do I separate the words? Here is my code:
code_dict = {'.-...': '&', '--..--': ',', '....-': '4', '.....': '5',
'...---...': 'SOS', '-...': 'B', '-..-': 'X', '.-.': 'R',
'.--': 'W', '..---': '2', '.-': 'A', '..': 'I', '..-.': 'F',
'.': 'E', '.-..': 'L', '...': 'S', '..-': 'U', '..--..': '?',
'.----': '1', '-.-': 'K', '-..': 'D', '-....': '6', '-...-': '=',
'---': 'O', '.--.': 'P', '.-.-.-': '.', '--': 'M', '-.': 'N',
'....': 'H', '.----.': "'", '...-': 'V', '--...': '7', '-.-.-.': ';',
'-....-': '-', '..--.-': '_', '-.--.-': ')', '-.-.--': '!', '--.': 'G',
'--.-': 'Q', '--..': 'Z', '-..-.': '/', '.-.-.': '+', '-.-.': 'C', '---...': ':',
'-.--': 'Y', '-': 'T', '.--.-.': '#', '...-..-': '$', '.---': 'J', '-----': '0',
'----.': '9', '.-..-.': '"', '-.--.': '(', '---..': '8', '...--': '3'
}
def decodeMorse(morseCode):
results = []
for item in morseCode.split(' '):
results.append(code_dict.get(item))
results = ''.join(results)
return results.lower()
morseCode = input('Message: ')
print(decodeMorse(morseCode))
Edit:
hello my name is, is:
.... . .-.. .-.. --- -- -.-- -. .- -- . .. ...
when I run the decoder it gives me hellomynameis, I would like it to give me hello my name is
Your example made it not possible. You are not giving any other separator than a space in the input and so you are not able to divide words in any way.
Your solution is to give your input a word separator (for example (double space), then split with .split(" ") and loop tru words).
Other solution might be nltk library, which might have some special functions for that - but here I'm just guessing.

How randomized the order of characters of a password generator

Currently the code only randomized in sequence , first letters then numbers and lastly symbols i would like it to randomized in any order without sequence , how to implement?
#Password Generator Project
import string
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']
print("Welcome to the PyPassword Generator!")
nr_letters= int(input("How many letters would you like in your password?\n"))
nr_symbols = int(input(f"How many symbols would you like?\n"))
nr_numbers = int(input(f"How many numbers would you like?\n"))
l_password = ""
s_password = ""
n_password = ""
# Eazy Level - Order not randomised:5
# e.g. 4 letter, 2 symbol, 2 number = JduE&!91
for letter in range(nr_letters):
l_password += random.choice(letters)
for symbol in range(nr_symbols):
s_password += random.choice(symbols)
for number in range(nr_numbers):
n_password += random.choice(numbers)
final_pass = str(l_password) + str(s_password) + str(n_password)
print(f"Here is your password :{final_pass}")
#Hard Level - Order of characters randomised:
#e.g. 4 letter, 2 symbol, 2 number = g^2jk8&P
You can use random.shuffle to shuffle the result:
final_pass = str(l_password) + str(s_password) + str(n_password)
l = list(final_pass)
random.shuffle(l)
final_pass = ''.join(l)
You can use random.sample()
import random
password = "123435454"
password = ''.join(random.sample(password, len(password)))
print(password)

From text file to dictionary

I'm a txt file and taking the strings and making the first my key for my dictionary I'm creating and the rest will be my values as a tuple. There is header before hand and I've already made my code "ignore" it at the start.
Example of txt values:
"Ronald Reagan","1981","8","69","California","Republican"
"George Bush","1989","4","64","Texas","Republican"
"Bill Clinton","1993","8","46","Arkansas","Democrat"
I want to create dictionary that gives the following output:
{"Ronald Reagan": (1981,8,69,"California", "Republican") etc.}
This is what I currenltly have as my code :
def read_file(filename):
d={}
f= open(filename,"r")
first_line = f.readline()
for line in f:
#line=line.strip('"')
#line=line.rstrip()
data=line.split('"')
data=line.replace('"', "")
print(data)
key_data=data[0]
values_data= data[1:]
valuesindata=tuple(values_data)
d[key_data]=valuesindata
print(d)
read_file(filename)
The first print statement (I put it there just to see what the output at that point was and it gave me the following :
Ronald Reagan,1981,8,69,California,Republican
George Bush,1989,4,64,Texas,Republican
etc. By the time it gets to the second print statement it does the following:
{'R': ('o', 'n', 'a', 'l', 'd', ' ', 'R', 'e', 'a', 'g', 'a', 'n', ',', '1', '9', '8', '1', ',', '8', ',', '6', '9', ',', 'C', 'a', 'l', 'i', 'f', 'o', 'r', 'n', 'i', 'a', ',', 'R', 'e', 'p', 'u', 'b', 'l', 'i', 'c', 'a', 'n', '\n'), 'G': ('e', 'o', 'r', 'g', 'e', ' ', 'B', 'u', 's', 'h', ',', '1', '9', '8', '9', ',', '4', ',', '6', '4', ',', 'T', 'e', 'x', 'a', 's', ',', 'R', 'e', 'p', 'u', 'b', 'l', 'i', 'c', 'a', 'n', '\n')}
Also, I'm splitting it at the quotes because some of my strings contain a comma as part of the name, example : "Carl, Jr."
I'm not wanting to import the csv module, so is there a way to do that?
You can use the csv module like alecxe suggested or you can do it "manually" like so:
csv_dict = {}
with open(csv_file, 'r') as f:
for line in f:
line = line.strip().replace('"', '').split(',')
csv_dict[line[0]] = tuple(int(x) if x.isdigit() else str(x) for x in line[1:])
This will remove the double quotes, cast numerical values to int and create a dictionary of tuples.
The major problem in your code leading into this weird result is that data variable is a string, data[0] would give you the first character, data[1:] the rest - you needed to call split(",") to first split the string into the list.
I have a limitation to not import any modules.
The idea is to use split(",") to split each line into individual items and strip() to remove the quotes around the item values:
d = {}
with open(filename) as f:
for line in f:
items = [item.strip('"').strip() for item in line.split(",")]
d[items[0]] = items[1:]
print(d)
Prints:
{'Bill Clinton': ['1993', '8', '46', 'Arkansas', 'Democrat'],
'George Bush': ['1989', '4', '64', 'Texas', 'Republican'],
'Ronald Reagan': ['1981', '8', '69', 'California', 'Republican']}
FYI, using csv module from the standard library would make things much easier:
import csv
from pprint import pprint
d = {}
with open(filename) as f:
reader = csv.reader(f)
for row in reader:
d[row[0]] = row[1:]
pprint(d)
You can also use a dictionary comprehension:
d = {row[0]: row[1:] for row in reader}

Python MemoryError

I have a small script, generating a wordlist from given chars in python. But always gets a MemoryError after execution. Why is it stored in the ram? is there better way of code not using ram but giving a working output?
from itertools import product
chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k',
'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3',
'4', '5', '6', '7', '8', '9']
length = 8
result = ["".join(item) for item in product(*[chars]*length)]
for item in result:
print(item)
By putting square brackets around your generator, you tell Python to turn it into an actual list, in-memory. You don't really need all of the elements at once, do you?
Instead, turn your square brackets into parentheses and Python will keep it a generator, which will yield items only when requested:
>>> ("".join(item) for item in product(*[chars]*length))
<generator object <genexpr> at 0x2d9cb40>
>>> ["".join(item) for item in product(*[chars]*length)]
[1] 3245 killed ipython2
Take a look at the string module. It has a bunch of helpful constants:
import string
from itertools import product
chars = string.letters + string.digits
length = 8
result = (''.join(item) for item in product(*[chars], repeat=length))
for item in result:
print(item)

Categories

Resources