Szyfr Cezara

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. ABCDEFGHIJKLMNOPQRSTUVWXYZ

  • wybieramy 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 z alfabet2 na 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 ekran

    • std::cin – wczytywanie z klawiatury

  • #include <string> – udostępnia typ std::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;

    • nr to zmienna typu int – przechowuje numer pozycji litery w alfabecie (index w alfabet1).

  • 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 name ma 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 stringu name (typu std::size_t, ale w praktyce traktujemy to jak liczbę całkowitą).

Przykłady:

  • name = "ala"name.size() == 3

  • name = ""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 znaku name[i] w stringu alfabet1.

  • 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') → 0

  • alfabet1.find('b') → 1

  • alfabet1.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 z alfabet2 o tym samym indeksie, który znaleźliśmy w alfabet1.

  • szyfr += ... – dopisujemy ten znak na koniec stringa szyfr.

To oznacza:

„Zamień literę z alfabet1 na odpowiadający jej znak z alfabet2 i dołóż do końca zaszyfrowanego tekstu”.

Przykład:

alfabet1 = "abc..."
alfabet2 = "123..."
name = "ab"

Iteracje:

  • dla i = 0name[0] = 'a'

    • alfabet1.find('a') → 0

    • alfabet2[0] → '1′

    • szyfr = "1"

  • dla i = 1name[1] = 'b'

    • alfabet1.find('b') → 1

    • alfabet2[1] → '2′

    • szyfr = "12"

Na końcu:

std::cout << szyfr;

wypisze zaszyfrowany tekst.


10. Połączenie z szyfrem Cezara

Klasyczny szyfr Cezara:

  • alfabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ

  • przesunięcie: np. o 3 w prawo

  • każda litera ma zaszyfrowaną odpowiedniczkę na podstawie przesunięcia

Twój program:

  • ma alfabet1 i alfabet2,

  • każda litera z alfabet1 ma stałą odpowiedniczkę w alfabet2,

  • 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++

  1. Jakie wartości może przyjmować zmienna typu std::string w C++?

  2. „Tekst” w tym programie jest przechowywany w jakim typie zmiennej?

  3. Co oznacza instrukcja std::string imie = "olek";?

  4. Po co w programie jest dyrektywa #include <string>?

  5. Do czego służy #include <iostream> w tym kodzie?

  6. Co robi instrukcja std::cout << "Tekst do zaszyfrowania? \n";?

  7. Co znaczy wywołanie getline(std::cin, name); w programie?

  8. Czym różni się getline(std::cin, name) od std::cin >> name;?

  9. Jakiego typu są zmienne name i szyfr w podanym kodzie?

  10. Jaką wartość początkową ma zmienna szyfr po uruchomieniu programu?

  11. Co przechowuje zmienna alfabet1?

  12. Co przechowuje zmienna alfabet2?

  13. Dlaczego w programie używane są dwa różne „alfabety”: alfabet1 i alfabet2?

  14. Co oznacza, że std::string jest typem złożonym (klasą), a nie prostym typem jak int?

  15. Co oznacza std w zapisie std::string i std::cout?

  16. Jak można uprościć zapis std::string używając using namespace std;?

  17. Jakie podstawowe operacje można wykonywać na zmiennej typu std::string (wymień przykłady)?

  18. Co oznacza nagłówek pętli for(int i = 0; i < name.size(); i++)?

  19. Co oznacza zapis i++ w pętli for?

  20. Jakiego typu jest wartość zwracana przez name.size()?

  21. Co otrzymamy z wyrażenia name.size() dla tekstu "ala"?

  22. Dlaczego w warunku pętli użyto i < name.size(), a nie i <= name.size()?

  23. Co oznacza zapis name[i] wewnątrz pętli?

  24. Co to znaczy, że name jest indeksowany od zera?

  25. Jakie indeksy mają znaki w stringu "abc"?

  26. Co robi instrukcja nr = alfabet1.find(name[i]);?

  27. Co dokładnie zwraca alfabet1.find(name[i]), gdy znak istnieje w alfabet1?

  28. Co zwróci alfabet1.find(name[i]), gdy znak NIE występuje w alfabet1?

  29. Jaką rolę pełni zmienna nr w programie?

  30. Co robi instrukcja szyfr += alfabet2[nr];?

  31. Co oznacza operator += w kontekście typu std::string?

  32. Jak sprawdzić długość łańcucha alfabet1 w programie?

  33. Dlaczego długość alfabet1 i alfabet2 powinna być taka sama?

  34. Co się stanie, jeśli name[i] będzie spacją, a w alfabet1 nie ma spacji?

  35. Jak można zabezpieczyć program przed sytuacją, gdy alfabet1.find(name[i]) nic nie znajdzie?

  36. Czy program poprawnie zaszyfruje wielkie litery (np. A, B)? Uzasadnij.

  37. Jak można zmodyfikować program, aby ignorował wielkość liter (małe/wielkie)?

  38. Jak można zmodynić program, aby znaki, których nie ma w alfabet1, przepisywał bez szyfrowania?

  39. W jaki sposób można wypisać również oryginalny tekst obok zaszyfrowanego?

  40. Co się stanie z wynikiem szyfrowania, jeśli zmienisz kolejność znaków w alfabet1?

  41. Co się stanie z wynikiem szyfrowania, jeśli zmienisz kolejność znaków w alfabet2?

  42. Co krok po kroku robi funkcja std::getline, gdy użytkownik wpisuje tekst i naciska ENTER?

  43. Jak można sprawdzić w programie, czy zmienna name jest pusta (nie zawiera znaków)?

  44. Jak można policzyć, ile znaków ma zaszyfrowany tekst zapisany w zmiennej szyfr?

  45. Czym różni się użycie name[i] od name.at(i)?

  46. Co robiłaby instrukcja return 0; na końcu funkcji main()?

  47. Co to znaczy, że string jest sekwencją znaków ułożonych „jeden po drugim” w pamięci?

  48. W jaki sposób można na podstawie tego programu zrealizować operację „deszyfrowania” (powrotu z szyfr do zwykłego tekstu)?

  49. Czy zmienna typu std::string może przechowywać polskie znaki (ą, ę, ł, ś itd.)? Omów to krótko.

  50. Co to jest szyfr Cezara w teorii kryptografii?

  51. Na czym polega klasyczny mechanizm szyfru Cezara (co robimy z literami)?

  52. Czym szyfr Cezara różni się od ogólnego „szyfru podstawieniowego”?

  53. W jaki sposób podany program realizuje ideę szyfru Cezara lub szyfru podstawieniowego?

  54. Jak można zmodyfikować program tak, aby użyć klasycznego przesunięcia o 3 litery (typowy szyfr Cezara)?

  55. Jak możemy opisać rolę „indeksu litery w alfabecie” w szyfrowaniu Cezara?

  56. Dlaczego szyfr Cezara nie jest bezpieczny we współczesnej kryptografii?

  57. Jak można zmienić program, aby użytkownik sam podawał wartość przesunięcia (np. o ile liter szyfrować)?

  58. Jakie są zalety używania typu std::string przy implementacji szyfru, zamiast tablicy char[]?

  59. W jaki sposób można wykorzystać ten program jako przykład na lekcji o podstawach kryptografii?

  60. Dlaczego szyfrowanie typu Cezara (i Twój program) nazywamy szyfrowaniem przez podstawienie jednej litery inną literą?

Zostaw komentarz

Koszyk