Задать вопрос
@Reyfli

Как правильно написать код на Python для шифра «Цезарь»?

Захотел написать код для Python, чтобы шифровать слова, введённые после запуска.
Вот начальная версия кода:
def caesar_cipher(text, shift):
    result = ""

    for char in text:
        if char.isalpha():
            shift_base = ord('a') if char.islower() else ord('A')
            result += chr((ord(char) - shift_base + shift) % 26 + shift_base)
        else:
            result += char

    return result

text = input("Введите текст для шифрования: ")
shift = int(input("Введите сдвиг: "))
encrypted_text = caesar_cipher(text, shift)
print("Зашифрованный текст:", encrypted_text)


Но проблема в том, что английские и русские буквы (все 33, включая ё) не могут шифроваться одновременно, а также, что буква "ё" не шифруется должным образом вместе с буквой "я". Могут появляться буквы по типу эпсилон (?), а "я" меняться на б.

Вот вторая версия кода с костылём бля букв "ё и я", но хотелось бы (требуют), сделать без костылей:
def caesar_cipher(text,
                  shift):  # Объявляет функцию Цезарь которая принимает текст для шифрования и число на которое будет сдвинуто число
    result = ""  # строчка для добавления зашифрованного текста (куда будет добавляться)

    for char in text:  # цикл по каждому символу в исходном тексте
        if char.isalpha():  # Заглавная или строчная каждый символ (проверка)
            if char == 'ё':  # Проверка для символа ё
                result += chr(ord('ж'))  # Шифруем ё как ж
                continue
            elif char == 'Ё':  # Проверка для заглавной Ё
                result += chr(ord('Ж'))  # Шифруем Ё как Ж
                continue

            shift_base = ord('а') if char.islower() else ord('А')
            # Если буква строчная, shift_base устанавливается в ASCII-код символа а,
            # Иначе — в код А. Это нужно для правильного сдвига в зависимости от регистра буквы.
            result += chr((ord(char) - shift_base + shift) % 32 + shift_base)
            # В этой строке: ord(char) получает ASCII-код текущего символа.
            # ord(char) - shift_base приводит код к диапазону от 0 до 31.
            # + shift добавляет значение сдвига.
            # % 32 берет остаток от деления, чтобы обеспечить циклический сдвиг (т.е. после 'я' снова начинается 'а').
            # + shift_base возвращает код обратно в исходный диапазон, и chr() преобразует его обратно в символ.
            # Результирующий символ добавляется к строке result.
        else:
            result += char  # присваив с добавлением
            # Если текущий символ не буква (например, пробел или пунктуация), он просто добавляется к result без изменений.

    return result  # Возвращает зашифрованный текст.


text = input("Введите текст для шифрования: ")  # Запрашивает у пользователя ввод текста для шифрования.
shift = int(
    input("Введите сдвиг: "))  # Запрашивает у пользователя ввод значения сдвига и преобразует его в целое число.
encrypted_text = caesar_cipher(text, shift)
# Вызывает функцию caesar_cipher с введенными текстом и сдвигом, сохраняя результат в переменной encrypted_text.
print("Зашифрованный текст:", encrypted_text)  # Выводит зашифрованный текст на экран.


Прошу помочь написать код для шифра Цезара правильно и без костылей для исправления определённых букв, если кто-то разбирается в данной теме, чтобы можно было также выбирать степень переноса вперёд, а также все буквы английского и русского алфавита работали вместе.
  • Вопрос задан
  • 1268 просмотров
Подписаться 1 Средний Комментировать
Решения вопроса 1
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Заведите строку со алфавитом, по которому идёт шифрование, и тогда вам будет без разницы, какие символы и сколько использовать. Если символа нет в алфавите, то оставляете его как есть. Если символ есть в алфавите, то нахо́дите его позицию, добавляете смещение по модулю длины алфавита, находите новый символ в вычисленной позиции.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@Vorono4ka
У Rsa97 отличная идея с алфавитом. Мне захотелось написать код с её реализацией.

У этой идеи есть небольшой нюанс (отнюдь, не минус, а порой даже плюс, если говорить о шифровании): без правильного алфавита расшифровать текст сразу не получится. Если это не нравится, то можно использовать для каждого языка свой алфавит, а не смешивать их воедино. Тогда алгоритм станет более похожим на классический шифр Цезаря, а значит и точный алфавит знать дешифровщику знать не нужно.

Если уходить от задачи с шифром Цезаря, можно перемешать алфавит любым идемпотентным алгоритом (чтобы каждый раз получать одинаковый результат). Тогда шифр станет более надёжным.

А если нет необходимости читать старые данные по прошествии времени, то можно вовсе менять ключ, по которому перемешивается алфавит, каждый день и получить этакую "Энигму".

В C, Java и других языках понадобится следующая формула для приведения shift в нужные границы (0 ≤ shift < len(alphabet)):
shift = (shift % len(alphabet) + len(alphabet)) % len(alphabet)


Первый modulo нужен для ограничения сдвига размером алфавита. Сложение результата с длиной алфавита необходимо для получения неотрицательного числа.

В Python же будет достаточно этого (см. как реализован modulo в Python):
shift %= len(alphabet)  # Или shift = shift % len(alphabet)


Но так как при индексации уже берется modulo, здесь никак не нужно обрабатывать shift заранее.

import string

alphabet = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя" 
alphabet += alphabet.upper()
alphabet += string.ascii_letters

# Также можно добавить к алфавиту string.punctuation и string.digits

def caesar_cipher(text: str, shift: int) -> str:
    return "".join(
        alphabet[(alphabet.index(char) + shift) % len(alphabet)] if char in alphabet else char 
        for char in text
    )

cipher = caesar_cipher("Привет, мир! Hello, world!", 46)
text = caesar_cipher(cipher, -46)  # Или len(alphabet) - 46
print(f"{cipher}\n{text}")


Итого получаем рабочий алгоритм и результат:
DЭХОСЯ, ЩХЭ! ъYеез, пзкеX!
Привет, мир! Hello, world!
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы