S. Это будет наш секрет

Шифр Цезаря, также известный как шифр сдвига, код Цезаря — один из самых простых и наиболее широко известных методов шифрования. Он назван в честь римского полководца Гая Юлия Цезаря, использовавшего его для секретной переписки со своими генералами.

Давайте реализуем эту систему шифрования. Однако для простоты мы будем сдвигать только латинские символы по кругу.

Формат ввода

Вводится размер сдвига для шифрования.

В файле public.txt содержится текст на английском языке.

Формат вывода

В файл private.txt запишите зашифрованный текст.

Пример

Ввод

# Пользовательский ввод
3

# Содержимое файла public.txt
Hello, world!

Вывод

# Содержимое файла private.txt
Khoor, zruog!

Ввод

# Пользовательский ввод
-10

# Содержимое файла public.txt
English alphabet: ABCDEFG...XYZ

Вывод

# Содержимое файла private.txt
Udwbyix qbfxqruj: QRSTUVW...NOP

Решение

Реализация шифра Цезаря не самая сложная задача. Ее можно поделить на две стадии – фильтрация текста для того, чтобы определить нужно ли изменять букву и непосредственно манипуляция с буквой, если да.

Проблему обычно представляет только сам процесс манипуляции. Есть два основных подхода – Работа через функции chr/ord и работа через списки/словари на уровне создания алфавита подмен.

Метод работы через chr/ord довольно громоздкий и недостаточно универсальный в силу того, что коды букв в файлах на разных платформах могут очень сильно отличаться. Ко мне обращались ученики, которые протестировав программу на windows сталкивались с тем, что она не срабатывала при попытке сдать тест. Все дело в том, то они забывали указать параметр encoding при чтении файла и работали с буквами в родной для windows кодировке CP1251, где коды символов очень сильно отличаются от кодов символов родной для пайтона UTF-8. Если бы работа шла через словари и списки, то неладное заподозрили бы сильно раньше, потому что в python ‘a’ будет означать букву а именно в кодировке UTF-8.

Итак в чем состоит идея шифра Цезаря? Перебирая буквы, мы определяем нужно ли ее кодировать. Если да, то к коду буквы прибавляется число, равное сдвигу, после чего получившийся код преобразуется в букву. Так как английские буквы расположены по-порядку то таким нехитрым способом можно реализовать требуемый сдвиг всего алфавита. Единственная проблема – буквы, расположеные в конце алфавита. Дело в том, что при сдвиге буква может “уйти” из диапазона букв алфавита. Поэтому при добавлении сдвига важно контролировать, чтобы буква оставалась в диапазоне кодов, и если новое значение кода превысило код последней буквы алфавита, вычитать из кода размер алфавита, возвращая, таким образом код в начало алфавита. В этом методе есть и другая проблема – так как у нас есть отдельно прописные и строчные буквы, нам потребуется отслеживать два диапазона. Кроме того, некоторые языки(включая русский) содержат отдельные от остальных буквы.

При использовании списков или словарей, мы сильно упрощаем как сам расчет, так и контроль границ – буквы после сдвига просто не могут выйти за пределы алфавита, так как мы всегда будем получать правильный готовый результат. Но сначала нам нао подготовить две сущности – сам алфавит abcdefghijklmnopqrstuvwxyzи его “сдвинутую” версию. В качестве примера возьмем сдвиг из первого тестового случая. У нас это будет три. Таким образом, отрезаем три буквы от начала алфавита и переклеиваем их в конец – defghijklmnopqrstuvwxyzabc. Теперь для кодирования нам достаточно вычислить позицию буквы в первом списке, и взять букву, с тем же порядковым номером из второго. Остается только решить проблему с заглавными буквами, ведь наш с вами алфавит не содержит заглавных букв. Эта проблема решается очень просто. При проверке на принадлежность алфавиту мы все символы приводим к нижнему регистру. а при кодировании проверяем, если буква в исходном тексте была заглавная, то переводим букву второго алфавита в верхний регистр. Мы это уже проделывали в задачах J. Транслитерация и F. Транслитерация 2.0.

Еще больше упростить задачу можно, если создать “словарь замен”. Для этого просто объединяем два списка в словарь, где ключом будет исходная буква, а значением – буква из списка заменю.

Остается учесть всего один нюанс – значение сдвига может быть больше размера словаря, или отрицательным числом. В этом случае достаточно получить модуль от деления сдвига на размер словаря.

Посмотреть код

Решение

Python
file_in = 'public.txt'
file_out = 'private.txt'

a = ord('a')
z = ord('z')

shift = int(input()) % 26

with open(file_in, encoding='UTF-8') as file:
    data = file.read()

    output = ''

    for i in range(len(data)):
        pos = a
        if a <= (code := ord(data[i].lower())) <= z:
            pos = code + shift
            if pos > z:
                pos -= 26
            elif pos < a:
                pos += 26
            output += chr(pos).upper() if data[i].isupper() else chr(pos)
        else:
            output += data[i]

# print(output)
with open(file_out, 'w', encoding='UTF-8') as file:
    file.write(output)

Решение

Python
file_in = 'public.txt'
file_out = 'private.txt'

alphabet = 'abcdefghijklmnopqrstuvwxyz'

shift = int(input()) % len(alphabet)

shifted_alphabet = alphabet[shift:] + alphabet[:shift]
output = ''

with open(file_in, encoding='UTF-8') as file:
    data = file.read()

    for char in data:
        new_char = char
        if char.lower() in alphabet:
            pos = alphabet.find(char.lower())
            new_char = shifted_alphabet[pos]
        output += new_char.upper() if char.isupper() else new_char

# print(output)
with open(file_out, 'w', encoding='UTF-8') as file:
    file.write(output)

Решение

Python
file_in = 'public.txt'
file_out = 'private.txt'

chars = 'abcdefghijklmnopqrstuvwxyz'

shift = int(input()) % len(chars)

shifted_chars = chars[shift:] + chars[:shift]

alphabet = {key: value for key, value in zip(chars, shifted_chars)}
output = ''

with open(file_in, encoding='UTF-8') as file:
    data = file.read()

    for char in data:
        new_char = alphabet[char.lower()] if char.lower() in alphabet else char
        output += new_char.upper() if char.isupper() else new_char

# print(output)
with open(file_out, 'w', encoding='UTF-8') as file:
    file.write(output)
Подписаться
Уведомить о
guest
20 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
nmalkam
24.12.2023 10:26

полезное))

21260-ascii-chart-javascript-red1
nmalkam
Ответить на  Сергей Клочко
27.12.2023 09:28

до меня не сразу дошло что кодировка и таблица ascii это разные вещи

то стоит смотреть в сторону “словарей”

имеется в виду написать словарь вида

LETTERS = {
1: ‘a’
и тд.
}

спасибо за ваш комментарий и за ссылку
буду пользоваться))))

nmalkam
Ответить на  Сергей Клочко
27.12.2023 09:53

ааааааа

я просто стараюсь пока что сам придумать решение, поэтому задаю вопросы, ещё не прочитав статью

спасибо что всё объясняете

думал оставить два последних абзаца на ещё одну попытку решения ))) теперь с полным знанием буду решать эту задачу последний раз))

nmalkam
27.12.2023 09:30

def caesar_secret():
  “””Это будет наш секрет
  здесь я решаю задачу путём высчитывания правильного ord в диапазоне 65 – 90 и 97 – 122
  разница с первым вариантом в том что, смещение может быть очень большим
  буквы неподвижны, цифры высчитываются (грубо говоря, зациклено)

  черновик взят из S_dict_v2.py”””
  ascii_lowercase = ‘abcdefghijklmnopqrstuvwxyz’
  ascii_uppercase = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’
  shift = int(input())
  # shift = 3
  # shift = -10
  # shift = -10000
  decoded = ”
  n_sign = 1
  if shift < 0:
    n_sign = -1
  shifted_num = (abs(shift) – abs(((abs(shift) // 26) * 26))) * n_sign
  with open(‘public.txt’, ‘r’, encoding=’utf-8′) as public:
    for num, line in enumerate(public):
      # if num == 1:
      #   shift = -10
      for letter in line:
        if letter.isalpha():
          num_ascii = ord(letter)
          if letter.isupper():
            new_num = (num_ascii – 65) + shifted_num
            if new_num >= 26:
              new_num -= 26
            decoded += ascii_uppercase[new_num]
          else:
            new_num = (num_ascii – 65 – 32) + shifted_num
            if new_num >= 26:
              new_num -= 26
            decoded += ascii_lowercase[new_num]
        else:
          decoded += letter
    with open(‘private.txt’, ‘w’, encoding=’utf-8′) as private:
      private.write(decoded)

caesar_secret()
# Udwbyix qbfxqruj: QRSTUVW…NOP
# Khoor, zruog!
с третьей попытки решил задачу

с помощью вашего готового кода понял что не прохожу тест со значением -1000

nmalkam
27.12.2023 12:15

shift = int(input()) % 26

это плюс python или нет?
Интересно узнать ваше мнение, Сергей

a = 10000 % 26 = 16
a_minus = -10000 % 26 = 10

https://silvertests.ru/GuideView.aspx?id=32160

nmalkam
Ответить на  Сергей Клочко
03.01.2024 13:22

К слову, вариантов поведения этой операции больше двух. Если мне не изменяет память, то их пять. Но наиболее распространены именно два.

рано я огорчился из-за двух вариантов, хотелось бы быть готовым))
полагаю остальные три варианта совсем совсем редкие??
они будут верными?

в numlock calculator при настройке символов, разделяющих целую и дробную часть числа калькулятор перестает правильно считать: 5 / 2 = 2

Дмитрий
04.03.2024 15:29

“chr/ord ljdjkmyj громоздкий” пожалуйста измените раскладку

victim
victim
28.03.2024 11:40

Можете подсказать, почему моё решение на третьем тесте сваливается?
a = [“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”, “k”, “l”, “m”, “n”, “o”, “p”, “q”, “r”,
     “s”, “t”, “u”, “v”, “w”, “x”, “y”, “z”]
c = int(input())
with open(“public.txt”, “r”) as f:
    d = f.read()
final = “”

for symbol in d:
    if symbol.lower() in a:
        if symbol in a:
            final += a[a.index(symbol) + c]
        else:
            final += a[a.index(symbol.lower()) + c].capitalize()
    else:
        final += symbol
with open(“private.txt”, “w”) as f:
    f.write(final)

konchenayaregistracia
konchenayaregistracia
20.06.2024 08:15

Вот еще одно решение:

shift = int(input())
with open(“public.txt”, “r”) as file:
text = file.read()
out = ”.join(
chr((ord(char) – 97 + shift) % 26 + 97)
if char.islower()
else chr((ord(char) – 65 + shift) % 26 + 65)
if char.isupper()
else char
for char in text
)
with open(“private.txt”, “w”) as file:
file.write(out)

Иван
Иван
16.08.2024 21:21

Программа проходит все тесты кроме 7, кто может подсказать в чем может быть проблема?
from math import ceil
althabet = [
    ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’,
    ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’,
    ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’]

replace_index = int(input())

with open(‘public.txt’, encoding=”UTF-8″) as file:
    text = file.read()

cycled_althabet = ceil(len(text) / len(althabet)) * althabet
result = ”

for i, char in enumerate(text): 
    if char.lower() in cycled_althabet:
        shifr = cycled_althabet[althabet.index(char.lower()) + replace_index]
        result += shifr.upper() if char.isupper() else shifr
    else:
        result += char
with open(‘private.txt’, ‘w’, encoding=”UTF-8″) as file:
    file.write(result)

Андрей Петрушенко
28.08.2024 08:31

Считая диапазон между ‘Z’ и ‘A’ через ord(), можно отвязаться и от системы, и от словарей.

Нужно гарантировать лишь что:
алфавит кодировки в правильной последовательностипорядок и количество прописных и заглавных букв одинаковы
shift = int(input())

decoded = ”

with open(‘public.txt’, encoding=’UTF-8′) as file_in:
for char in file_in.read():
if ‘A’ <= char.upper() <= ‘Z’:
start_ord = ord(‘A’) if char.isupper() else ord(‘a’)
char_new_ord = start_ord + (ord(char) – start_ord + shift) % (ord(‘Z’) – ord(‘A’) + 1)
decoded += chr(char_new_ord)
else:
decoded += char

with open(‘private.txt’, ‘w’, encoding=’UTF-8′) as file_out:
file_out.write(decoded)