Szyfr Cezara to jeden z najprostszych szyfrów w historii kryptografii.
Legenda mówi, że używał go Juliusz Cezar, aby wysyłać tajne wiadomości do swoich dowódców.
Zasada jest bardzo prosta:
mamy alfabet, np.
ABCDEFGHIJKLMNOPQRSTUVWXYZwybieramy przesunięcie, np. o 3 litery
każdą literę zastępujemy literą oddaloną o 3 pozycje w prawo
Przykład (przesunięcie o 3):
A → D
B → E
C → F
X → A
Y → B
Z → C
Słowo ALA po zaszyfrowaniu może wyglądać np. tak: DOD.
To oznacza, że każda litera w tekście jawnym jest zamieniana na inną literę według prostego, powtarzalnego wzoru (przesunięcie w alfabecie).
Ten typ szyfrowania nazywamy monoalfabetycznym szyfrem podstawieniowym – każda litera ma zawsze tę samą „zaszyfrowaną odpowiedniczkę”.
2. Jak to się ma do naszego programu?
Twój program nie realizuje dokładnie „klasycznego” przesunięcia o 3 litery, ale robi coś bardzo podobnego:
mamy alfabet1 – litery, które wolno wpisywać
mamy alfabet2 – znaki szyfrujące, które odpowiadają tym literom
każdą literę z
name(podany tekst) zamieniasz na inny znak zalfabet2na tej samej pozycji.
Czyli:
zamiast „przesuwać literę o kilka miejsc w alfabecie”,
Ty mapujesz litery z jednego „alfabetu” na drugi.
To nadal jest szyfr podstawieniowy – idea ta sama:
Znak jawny → zawsze ten sam znak zaszyfrowany.
Możesz uczniom powiedzieć tak:
„Klasyczny szyfr Cezara przesuwa litery o kilka miejsc, a nasz program robi podobną rzecz, tylko zamiast przesuwania używa gotowej tabelki zamian (alfabet1 → alfabet2).”
3. Kod programu – przegląd
Twój kod:
#include <iostream>
#include <string>
int main()
{
std::string name,szyfr;
int nr = 0;
std::string alfabet1 = "abcdefghijklmnoprstuw";
std::string alfabet2 = "1234567890!@#$%^&*()Q";
std::cout << "Tekst do zaszyfrowania? \n";
getline (std::cin, name);
for(int i =0; i< name.size();i++) {
nr = alfabet1.find(name[i]);
szyfr += alfabet2[nr];
}
std::cout << szyfr;
}
Spróbujmy go rozbić na kawałki i przy okazji podpiąć pod Twoje pytania.
4. Nagłówki – dlaczego są potrzebne?
#include <iostream>
#include <string>
#include <iostream>– udostępnia strumienie wejścia/wyjścia, m.in.std::cout– wypisywanie na ekranstd::cin– wczytywanie z klawiatury
#include <string>– udostępnia typstd::string, czyli:ciąg znaków (tekst),
na którym mamy metody typu
size(),find(),at(), itp.
To łączy się z Twoimi pytaniami:
„Tekst to jaki typ zmiennej?” – w tym programie to właśnie std::string.
5. Funkcja main i deklaracja zmiennych
int main()
{
std::string name,szyfr;
int nr = 0;
std::string alfabet1 = "abcdefghijklmnoprstuw";
std::string alfabet2 = "1234567890!@#$%^&*()Q";
...
}
int main()– główna funkcja programu. Tutaj wszystko się zaczyna.std::string name, szyfr;name– tekst, który użytkownik wpisze z klawiatury,szyfr– wynik zaszyfrowania (początkowo pusty string).
Na pytanie: „Jakie wartości może przyjmować string?” –std::string może przechowywać dowolny ciąg znaków, np. "Ala", "test123", "ala ma kota", nawet pusty tekst "".
int nr = 0;nrto zmienna typuint– przechowuje numer pozycji litery w alfabecie (index walfabet1).
std::string alfabet1 = "abcdefghijklmnoprstuw";to „zwykły” alfabet, ale uproszczony (bez wszystkich liter, np. bez „q”, „y”, „z”).
std::string alfabet2 = "1234567890!@#$%^&*()Q";alfabet szyfrujący – każdy znak ma indeks odpowiadający literze w
alfabet1.
Czyli:
alfabet1[0]→alfabet2[0]alfabet1[1]→alfabet2[1]itd.
To jest Twoja tabelka podstawień – odpowiednik „przesunięcia” w szyfrze Cezara.
6. Wczytywanie tekstu – getline(std::cin, name);
std::cout << "Tekst do zaszyfrowania? \n";
getline (std::cin, name);
std::cout << "Tekst do zaszyfrowania? \n";wypisuje pytanie na ekran,
\n– znak nowej linii.
getline(std::cin, name);wczytuje całą linię tekstu (aż do naciśnięcia ENTER),
zapisuje ją do zmiennej
name.
To jest odpowiedź na Twoje pytanie:
„Co znaczy getline (std::cin, name);?”
Wczytaj cały wiersz tekstu z klawiatury (łącznie ze spacjami) i zapisz go jako string w zmiennej
name.
Różnica do std::cin >> name; jest taka, że cin >> zatrzymuje się na pierwszej spacji, a getline bierze wszystko do końca linii.
7. Pętla for i name.size()
for(int i =0; i< name.size();i++) {
nr = alfabet1.find(name[i]);
szyfr += alfabet2[nr];
}
7.1. Nagłówek pętli
for(int i = 0; i < name.size(); i++)
int i = 0;– zmienna sterująca pętlą, startuje od 0.i < name.size();– warunek kontynuowania:name.size()zwraca długość stringa (liczbę znaków),jeśli
namema np. 5 znaków,name.size()= 5,indeksy znaków to: 0,1,2,3,4,
dlatego warunek jest
i < size(), a nie<=.
To odpowiada na pytanie:
„name.size() co otrzymamy?”
name.size()zwraca liczbę znaków w stringuname(typustd::size_t, ale w praktyce traktujemy to jak liczbę całkowitą).
Przykłady:
name = "ala"→name.size() == 3name = ""→name.size() == 0
7.2. name[i] – pojedynczy znak
W środku pętli używasz:
name[i]
name[i]oznacza i-ty znak w stringu,przy
i = 0– pierwszy znak,przy
i = 1– drugi znak itd.
8. Szukanie znaku w alfabecie – alfabet1.find(name[i])
nr = alfabet1.find(name[i]);
To jest kluczowa linijka dla szyfrowania.
alfabet1.find(name[i])szuka znakuname[i]w stringualfabet1.Jeśli znajdzie, zwraca numer pozycji (indeks) tego znaku.
Ten numer zapisujemy do zmiennej
nr.
Możesz to tłumaczyć uczniom tak:
alfabet1.find(name[i])mówi nam: „którą literą w alfabecie jest aktualny znak?”.
To dokładnie Twoje pytanie:
„zwraca która litera jest w alfabecie alfabet1.find(name[i])” – TAK, właśnie to robi.
Przykład, jeśli:
std::string alfabet1 = "abcdefghijklmnoprstuw";
alfabet1.find('a')→ 0alfabet1.find('b')→ 1alfabet1.find('c')→ 2
Jeśli znak nie istnieje w alfabet1, metoda find() zwróci specjalną wartość std::string::npos (bardzo dużą liczbę).
W Twoim kodzie zakładamy, że użytkownik wpisuje tylko litery znajdujące się w alfabet1.
9. Budowanie zaszyfrowanego tekstu – szyfr += alfabet2[nr];
szyfr += alfabet2[nr];
alfabet2[nr]– bierzemy znak zalfabet2o tym samym indeksie, który znaleźliśmy walfabet1.szyfr += ...– dopisujemy ten znak na koniec stringaszyfr.
To oznacza:
„Zamień literę z
alfabet1na odpowiadający jej znak zalfabet2i dołóż do końca zaszyfrowanego tekstu”.
Przykład:
alfabet1 = "abc..."
alfabet2 = "123..."
name = "ab"
Iteracje:
dla
i = 0→name[0] = 'a'alfabet1.find('a')→ 0alfabet2[0]→ '1′szyfr = "1"
dla
i = 1→name[1] = 'b'alfabet1.find('b')→ 1alfabet2[1]→ '2′szyfr = "12"
Na końcu:
std::cout << szyfr;
wypisze zaszyfrowany tekst.
10. Połączenie z szyfrem Cezara
Klasyczny szyfr Cezara:
alfabet:
ABCDEFGHIJKLMNOPQRSTUVWXYZprzesunięcie: np. o 3 w prawo
każda litera ma zaszyfrowaną odpowiedniczkę na podstawie przesunięcia
Twój program:
ma
alfabet1ialfabet2,każda litera z
alfabet1ma stałą odpowiedniczkę walfabet2,zamiast przesuwać, korzystasz z „tabelki zamian”.
Możesz powiedzieć uczniom:
Nasz kod realizuje szyfr podstawieniowy taki jak Cezara, tylko zamiast przesunięcia o 3 litery mamy ręcznie przygotowane odpowiadające sobie znaki w dwóch stringach.
Chcesz klasycznego Cezara w tej samej konwencji?
Możesz ustawić:
std::string alfabet1 = "abcdefghijklmnopqrstuvwxyz";
std::string alfabet2 = "defghijklmnopqrstuvwxyzabc"; // przesunięcie o 3
I reszta programu będzie działać tak samo – to już będzie „prawie podręcznikowy” szyfr Cezara.
11. Pytania kontrolne do tego artykułu
Na koniec możesz wykorzystać część z Twoich 60 pytań – tu masz mały pakiet „pod lekcję”:
60 pytań – szyfr Cezara i kod w C++
Jakie wartości może przyjmować zmienna typu
std::stringw C++?„Tekst” w tym programie jest przechowywany w jakim typie zmiennej?
Co oznacza instrukcja
std::string imie = "olek";?Po co w programie jest dyrektywa
#include <string>?Do czego służy
#include <iostream>w tym kodzie?Co robi instrukcja
std::cout << "Tekst do zaszyfrowania? \n";?Co znaczy wywołanie
getline(std::cin, name);w programie?Czym różni się
getline(std::cin, name)odstd::cin >> name;?Jakiego typu są zmienne
nameiszyfrw podanym kodzie?Jaką wartość początkową ma zmienna
szyfrpo uruchomieniu programu?Co przechowuje zmienna
alfabet1?Co przechowuje zmienna
alfabet2?Dlaczego w programie używane są dwa różne „alfabety”:
alfabet1ialfabet2?Co oznacza, że
std::stringjest typem złożonym (klasą), a nie prostym typem jakint?Co oznacza
stdw zapisiestd::stringistd::cout?Jak można uprościć zapis
std::stringużywającusing namespace std;?Jakie podstawowe operacje można wykonywać na zmiennej typu
std::string(wymień przykłady)?Co oznacza nagłówek pętli
for(int i = 0; i < name.size(); i++)?Co oznacza zapis
i++w pętlifor?Jakiego typu jest wartość zwracana przez
name.size()?Co otrzymamy z wyrażenia
name.size()dla tekstu"ala"?Dlaczego w warunku pętli użyto
i < name.size(), a niei <= name.size()?Co oznacza zapis
name[i]wewnątrz pętli?Co to znaczy, że
namejest indeksowany od zera?Jakie indeksy mają znaki w stringu
"abc"?Co robi instrukcja
nr = alfabet1.find(name[i]);?Co dokładnie zwraca
alfabet1.find(name[i]), gdy znak istnieje walfabet1?Co zwróci
alfabet1.find(name[i]), gdy znak NIE występuje walfabet1?Jaką rolę pełni zmienna
nrw programie?Co robi instrukcja
szyfr += alfabet2[nr];?Co oznacza operator
+=w kontekście typustd::string?Jak sprawdzić długość łańcucha
alfabet1w programie?Dlaczego długość
alfabet1ialfabet2powinna być taka sama?Co się stanie, jeśli
name[i]będzie spacją, a walfabet1nie ma spacji?Jak można zabezpieczyć program przed sytuacją, gdy
alfabet1.find(name[i])nic nie znajdzie?Czy program poprawnie zaszyfruje wielkie litery (np.
A,B)? Uzasadnij.Jak można zmodyfikować program, aby ignorował wielkość liter (małe/wielkie)?
Jak można zmodynić program, aby znaki, których nie ma w
alfabet1, przepisywał bez szyfrowania?W jaki sposób można wypisać również oryginalny tekst obok zaszyfrowanego?
Co się stanie z wynikiem szyfrowania, jeśli zmienisz kolejność znaków w
alfabet1?Co się stanie z wynikiem szyfrowania, jeśli zmienisz kolejność znaków w
alfabet2?Co krok po kroku robi funkcja
std::getline, gdy użytkownik wpisuje tekst i naciska ENTER?Jak można sprawdzić w programie, czy zmienna
namejest pusta (nie zawiera znaków)?Jak można policzyć, ile znaków ma zaszyfrowany tekst zapisany w zmiennej
szyfr?Czym różni się użycie
name[i]odname.at(i)?Co robiłaby instrukcja
return 0;na końcu funkcjimain()?Co to znaczy, że string jest sekwencją znaków ułożonych „jeden po drugim” w pamięci?
W jaki sposób można na podstawie tego programu zrealizować operację „deszyfrowania” (powrotu z
szyfrdo zwykłego tekstu)?Czy zmienna typu
std::stringmoże przechowywać polskie znaki (ą, ę, ł, ś itd.)? Omów to krótko.Co to jest szyfr Cezara w teorii kryptografii?
Na czym polega klasyczny mechanizm szyfru Cezara (co robimy z literami)?
Czym szyfr Cezara różni się od ogólnego „szyfru podstawieniowego”?
W jaki sposób podany program realizuje ideę szyfru Cezara lub szyfru podstawieniowego?
Jak można zmodyfikować program tak, aby użyć klasycznego przesunięcia o 3 litery (typowy szyfr Cezara)?
Jak możemy opisać rolę „indeksu litery w alfabecie” w szyfrowaniu Cezara?
Dlaczego szyfr Cezara nie jest bezpieczny we współczesnej kryptografii?
Jak można zmienić program, aby użytkownik sam podawał wartość przesunięcia (np. o ile liter szyfrować)?
Jakie są zalety używania typu
std::stringprzy implementacji szyfru, zamiast tablicychar[]?W jaki sposób można wykorzystać ten program jako przykład na lekcji o podstawach kryptografii?
Dlaczego szyfrowanie typu Cezara (i Twój program) nazywamy szyfrowaniem przez podstawienie jednej litery inną literą?
