#!/usr/bin/env python3 """ Szyfr książkowy (https://pl.wikipedia.org/wiki/Szyfr_Ottendorfa) jest to szyfr, w którym wiadomość zaszyfrowana jest w postaci zestawu liczb oznaczających lokalizację jej kolejnych liter bądź słów (zależnie od wariantu) w określonym tekście. Jedynie jednocześnie wiedząc, o który tekst chodzi i posiadając szyfr, odbiorca może odkodować przekazywaną informację. Napisz funkcję, która będzie implementowała pewien rodzaj tego szyfru (i używający jej program). Niech przyjmuje jako argumenty nazwy dwóch plików – z tekstem i z szyfrem, po czym niech wypisuje na ekran rozszyfrowaną wiadomość. Niech plik z szyfrem w każdej linii zawiera instrukcję do odszyfrowania kolejnej litery wiadomości: trzy liczby, odpowiadające kolejno linii, wyrazowi i literze. Przykładowo wykorzystując ten plik z kodem jako tekst oraz następujący szyfr zapisany w pliku "szyfr.txt": 14 6 3 3 1 2 10 6 3 6 6 6 4 13 2 8 3 5 1 1 2 powinniśmy otrzymać wiadomość "działa!". Zadanie dodatkowe: Napisz drugą funkcję, która znów pobierać będzie jako argumenty nazwy plików – z tekstem oraz plik, do którego zapisany zostanie zaszyfrowana wiadomość, a także string z wiadomością do zaszyfrowania. Niech wykonuje ona operację przeciwną niż poprzednia – wyszukuje w pliku z tekstem wystąpienia kolejnych liter szyfrowanej wiadomości i wpisuje ich położenia do pliku z szyfrem. W przypadku niepowodzenia niech wypisuje, których znaków nie udało się znaleźć w tekście. """ import sys import random def odszyfruj(tekst_sciezka,szyfr_sciezka): """ Funkcja do odszyfrowywania. Przyjmuje jako argumenty ścieżki/nazwy tekstu i szyfru. """ #Czytam plik z tekstem i tworzę listę z jego liniami: with open(tekst_sciezka) as tekst_plik: tekst = tekst_plik.readlines() #Czytam plik z szyfrem i tworzę listę z jego liniami: with open(szyfr_sciezka) as szyfr_plik: szyfr = szyfr_plik.readlines() wiadomosc = [] for szyfr_znak in szyfr: #Czytam kolejne "koordynaty" z szyfru linia,wyraz,litera = [int(i) - 1 for i in szyfr_znak.split()] #-1, bo Python numeruje od 0 #znajduję linię w tekście, dzielę ją na wyrazy, wybieram wyraz, a potem jego literę wiadomosc.append(tekst[linia].split()[wyraz][litera]) return "".join(wiadomosc) def zaszyfruj(tekst_sciezka,szyfr_sciezka,wiadomosc): """ Funkcja do zaszyfrowania (z podpunktu dodatkowego). Przyjmuje jako argumenty ścieżki/nazwy tekstu i szyfru, a także wiadomość do zaszyfrowania. Żeby utrudnić nieco osobie próbującej złamać szyfr wyszukanie charakterystycznych dla języka wzorców, dla każdej litery wyszukiwane są wszystkie jej wystąpienia w tekście, po czym do szyfru losowane jest jedno z nich. """ #Czytam plik z tekstem i tworzę listę z jego liniami: with open(tekst_sciezka) as tekst_plik: tekst = tekst_plik.readlines() #Otwieram plik z szyfrem do zapisu: with open(szyfr_sciezka,'w') as szyfr_plik: pozycje_liter = {} #słownik na wystąpienia każdej litery for wiadomosc_znak in wiadomosc: if wiadomosc_znak not in pozycje_liter: for nl, linia in enumerate(tekst): for nw, wyraz in enumerate(linia.split()): for nz, znak in enumerate(wyraz): if znak == wiadomosc_znak: pozycje_liter[znak] = pozycje_liter.get(znak,[])+[(nl+1,nw+1,nz+1)] wybor = pozycje_liter[wiadomosc_znak] #wszystkie pozycje danej litery wybor = wybor[random.randint(0,len(wybor)-1)] #wybieramy losową z nich wybor = [str(i) for i in wybor] #pozycja to krotka trzech integerów, zamieniamy je na str print(wiadomosc_znak," ".join(wybor)) szyfr_plik.write(" ".join(wybor)+"\n") def informacja(): """ Prosta funkcja wypisująca jak korzystać z programu. Wykorzystuję fakt, że postawione obok siebie stringi są automatycznie łączone w jeden. """ print('Szyfrowanie/odszyfrowywanie plików "szyfrem książkowym". Instrukcja użycia:\n' 'python3 szyfr_ksiazkowy.py TRYB PLIK_Z_TEKSTEM PLIK_Z_SZYFREM [WIADOMOŚĆ]\n' '\n' 'TRYB - jedno spośród:\n' ' -o : odszyfrowanie wiadomości zawartej w PLIK_Z_SZYFREM na podstawie PLIK_Z_TEKSTEM\n' ' -z : zaszyfrowanie WIADOMOŚĆ na podstawie PLIK_Z_TEKSTEM i zapisanie do PLIK_Z_SZYFREM') narg = len(sys.argv) """ Warto zauważyć, że gdyby poniżej zamienić kolejnością wyrażenia wokół operatora logicznego "and", program zwracałby błąd w przypadku jego wywołania bez dodatkowych argumentów (bo sys.argv[1] wówczas nie istnieje). Python działa "leniwie" i przestaje sprawdzać "and" gdy tylko natrafi na pierwszy fałsz. Dlatego też jeśli "narg" != 4 (bądź 5), to dalsza część (z sys.argv[1]) nie jest nawet wykonywana, bo niezależnie od niej całe wyrażenie i tak musi mieć wartość "fałsz". """ if narg == 4 and sys.argv[1] == "-o": print(odszyfruj(sys.argv[2],sys.argv[3])) elif narg == 5 and sys.argv[1] == "-z": zaszyfruj(sys.argv[2],sys.argv[3],sys.argv[4]) else: informacja() #coś było nie tak, wypisz informację z instrukcją użycia