Przejdź do treści

Szyfr Cezara w Pythonie

Cała funkcja ma dziesięć linijek. Skopiuj ją albo wpisz własny tekst i zobacz, co kod robi z każdą literą.

Python 3
def caesar(text, shift):    result = ""    for char in text:        if char.isascii() and char.isalpha():            base = ord("A") if char.isupper() else ord("a")            index = (ord(char) - base + shift) % 26            result += chr(index + base)        else:            result += char    return result print(caesar("Witaj świecie!", 3))# Zlwdm śzlhflh!
reklama

Jak działa ten kod

Pętla czyta tekst znak po znaku. Funkcja ord() zamienia literę na liczbę: ord("A") to 65, a ord("a") to 97. Po odjęciu tej podstawy dostajesz miejsce litery w alfabecie, od 0 do 25.

Po dodaniu przesunięcia % 26 zawija wynik, więc przy przesunięciu 3 litera Z trafia na C. Na koniec chr() robi z liczby z powrotem literę. Jeśli sam szyfr jest dla ciebie nowy, zajrzyj do przewodnika, w którym nie ma kodu.

Spacje, znaki interpunkcyjne i cyfry idą gałęzią else i zostają bez zmian. To samo dotyczy polskich liter, dlatego „ś” w przykładzie się nie zmienia. Odpowiada za to warunek isascii(): bez niego Python uzna ś za zwykłą małą literę, wyliczy z niej przypadkową literę A–Z i tekstu nie da się już odszyfrować.

Deszyfrowanie w Pythonie

Druga funkcja nie jest potrzebna. Odszyfrowanie to takie samo przesunięcie w drugą stronę, więc wystarczy podać liczbę ujemną:

Python
print(caesar("Zlwdm śzlhflh!", -3))# Witaj świecie!

Działa to dzięki temu, że % w Pythonie nie zwróci tu liczby ujemnej: (0 - 3) % 26 daje 23, czyli A wraca na X. W C, Javie i JavaScripcie wyszłoby -3, dlatego wersja w JavaScripcie potrzebuje dodatkowego kroku.

Program pobierający dane od użytkownika

Zadania domowe często wymagają programu, który wczytuje tekst i przesunięcie z klawiatury. Dopisz to pod funkcją:

Python
text = input("Tekst: ")shift = int(input("Przesunięcie: "))mode = input("Szyfrować czy odszyfrować? (s/o): ") if mode == "o":    shift = -shift print(caesar(text, shift))

input() zawsze zwraca napis, więc przesunięcie trzeba przepuścić przez int(). Jeśli ktoś wpisze słowo zamiast liczby, Python zatrzyma się z błędem ValueError.

Łamanie szyfru metodą brute force

Przy 26 literach do sprawdzenia jest tylko 25 przesunięć, więc krótka pętla odczyta wiadomość bez klucza. Wystarczy znaleźć w wynikach linijkę, która ma sens:

Python
def crack(text):    for shift in range(1, 26):        print(shift, caesar(text, -shift)) crack("Zlwdm śzlhflh!")# 1 Ykvcl śykgekg!# 2 Xjubk śxjfdjf!# 3 Witaj świecie!# ...

Więcej o tej metodzie przeczytasz w artykule o ataku brute force, a łamacz online zrobi to samo w przeglądarce.

Krótsza wersja z str.translate

Gdy rozumiesz już pętlę, możesz zastąpić ją tabelą zamiany. str.maketrans() łączy każdą literę z jej przesuniętą wersją, a translate() stosuje tę tabelę do całego tekstu. Polskie litery także tutaj zostają bez zmian.

Python
from string import ascii_lowercase as lower, ascii_uppercase as upper def caesar(text, shift):    shift %= 26    table = str.maketrans(        lower + upper,        lower[shift:] + lower[:shift] + upper[shift:] + upper[:shift],    )    return text.translate(table)

Wyniki są takie same, a przy długich tekstach zwykle szybciej, bo tabela powstaje tylko raz. W pierwszym zadaniu łatwiej jednak wytłumaczyć pętlę.

Szyfr Cezara z polskimi znakami

Żeby przesuwać także ą, ć czy ż, zapisz alfabet w napisie i szukaj każdego znaku przez index(). To ten sam alfabet, 35 znaków, którego używa nasze narzędzie:

Python
ALPHABET = "aąbcćdeęfghijklłmnńoópqrsśtuvwxyzźż" def caesar_alphabet(text, shift):    result = ""    for char in text:        letter = char.lower()        if letter in ALPHABET:            index = (ALPHABET.index(letter) + shift) % len(ALPHABET)            new = ALPHABET[index]            result += new.upper() if char.isupper() else new        else:            result += char    return result print(caesar_alphabet("Zażółć gęślą jaźń", 3))# Acbrńę jhvnć łcąp

Zawijanie korzysta teraz z len(ALPHABET), więc nie musisz nigdzie wpisywać 35. Wynik możesz porównać w narzędziu szyfru Cezara po wybraniu polskiego alfabetu.

Szyfrowanie pliku tekstowego

Wczytaj plik, przekaż jego zawartość do funkcji i zapisz wynik w nowym pliku. Ustawienie encoding="utf-8" sprawia, że polskie znaki nie zepsują się w żadnym systemie.

Python
with open("message.txt", encoding="utf-8") as source:    text = source.read() with open("encrypted.txt", "w", encoding="utf-8") as target:    target.write(caesar(text, 3))

Ten sam szyfr jako klasa

Klasa przydaje się, gdy jednego klucza używasz wiele razy albo gdy prowadzący wymaga kodu obiektowego. Przechowuje przesunięcie i korzysta z funkcji z góry strony:

Python
class CaesarCipher:    def __init__(self, shift=3):        self.shift = shift     def encrypt(self, text):        return caesar(text, self.shift)     def decrypt(self, text):        return caesar(text, -self.shift) cipher = CaesarCipher(5)print(cipher.encrypt("Witaj świecie!"))# Bnyfo śbnjhnj!

Testowanie kodu

Kilka linijek z assert wyłapie większość błędów: przejście poza Z, zachowanie wielkości liter, pozostawienie interpunkcji i powrót do tekstu wyjściowego. Jeśli wszystko gra, program wypisze OK.

Python
assert caesar("ABC", 3) == "DEF"assert caesar("xyz", 3) == "abc"assert caesar("Hello, World!", 5) == "Mjqqt, Btwqi!"assert caesar(caesar("Python", 7), -7) == "Python"assert caesar("A", 26) == "A"assert caesar("é", 3) == "é"print("OK")

Najczęstsze błędy

Klasyczny błąd to chr(ord(char) + shift) bez modulo: przy przesunięciu 3 litera Z zamienia się wtedy w symbol ]. Inny to użycie podstawy 65 dla wszystkich liter, co psuje małe litery. Początkujący zapominają też o int() przy input(), a wtedy Python zatrzymuje się z błędem TypeError przy dodawaniu przesunięcia.

Najczęstsze pytania

Jakiej wersji Pythona potrzebuję?

Pythona 3.7 lub nowszego, bo wtedy pojawiło się isascii(). Kod z tej strony uruchomiliśmy na Pythonie 3.9. Nie wymaga dodatkowych pakietów.

Czy mogę tym chronić prawdziwe dane?

Nie. Pętla pokazana wyżej łamie ten szyfr w kilka sekund. Używaj go do nauki i łamigłówek, a hasła i prywatne dane chroń współczesnym szyfrowaniem z utrzymywanej biblioteki.

Jak sprawdzić, czy wynik jest poprawny?

Wpisz ten sam tekst i przesunięcie w narzędziu szyfru Cezara i porównaj. Ze swoich testów możesz też wywołać darmowe REST API.

Co dalej