-
Notifications
You must be signed in to change notification settings - Fork 47
Orlov 4 tasks #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Orlov 4 tasks #21
Changes from all commits
726c841
e7e9ccb
bc6307a
949592e
e5c3791
8fc8b7f
c3bcf3c
ccd2e3f
1ed623d
9a7980c
bb6813e
85c28d1
ed8b75b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.javarush.orlov; | ||
|
|
||
| import java.util.ArrayList; | ||
|
|
||
| public class BruteForce extends Encrypt { | ||
|
|
||
| // Мини-словарь частых слов для проверки корректности расшифровки | ||
| private static final String[] DICTIONARY = {"и", "в", "на", "что", "как", "это", "он", "она", "они"}; | ||
|
|
||
| // Метод перебирает все возможные ключи шифра Цезаря и выбирает лучший | ||
| // Лучший ключ определяется по количеству слов из словаря, найденных в тексте | ||
| public ArrayList<String> bruteForce(ArrayList<String> text) { | ||
| ArrayList<String> result; | ||
| int key = 0; // ключ с наибольшим количеством совпадений | ||
| int count = 0; // максимальное количество совпадений слов из словаря | ||
|
|
||
| // Перебираем все возможные ключи (все позиции алфавита) | ||
| for (int i = 0; i < ALPHABET.length; i++) { | ||
| result = super.encrypt(text, i); // "расшифровываем" текст с текущим ключом | ||
| int temp = extracted(result); // подсчитываем совпадения слов из словаря | ||
| if (temp > count) { | ||
| key = i; // если найдено больше совпадений, сохраняем ключ | ||
| count = temp; | ||
| } | ||
| } | ||
|
|
||
| // Возвращаем текст, расшифрованный с лучшим ключом | ||
| return super.encrypt(text, key); | ||
| } | ||
|
|
||
| // Метод подсчитывает количество слов из словаря в списке строк | ||
| private static int extracted(ArrayList<String> result) { | ||
| int count = 0; | ||
|
|
||
| // Перебираем все строки текста | ||
| for (int j = 0; j < result.size(); j++) { | ||
| count += checkWord(result.get(j)); // считаем совпадения слов из словаря в каждой строке | ||
| } | ||
|
|
||
| return count; | ||
| } | ||
|
|
||
| // Метод проверяет строку на наличие слов из словаря | ||
| private static int checkWord(String str) { | ||
| int count = 0; | ||
|
|
||
| // Разбиваем строку на слова по пробелам | ||
| String[] word = str.split(" "); | ||
|
|
||
| for (int i = 0; i < word.length; i++) { | ||
| // Убираем все символы кроме букв | ||
| word[i] = word[i].replaceAll("[^а-яА-Я]", ""); | ||
|
|
||
| // Сравниваем слово с каждым словом из словаря (игнорируя регистр) | ||
| for (int j = 0; j < DICTIONARY.length; j++) { | ||
| if (word[i].equalsIgnoreCase(DICTIONARY[j])) { | ||
| count++; // если совпадение найдено, увеличиваем счетчик | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return count; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| package com.javarush.orlov; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.ArrayList; | ||
|
|
||
| public class CaesarCipherApp { | ||
|
|
||
| public static void main(String[] args) throws IOException { | ||
|
|
||
| // Главный цикл программы, пока пользователь не выберет выход | ||
| while (true) { | ||
| int num = Validator.begin(); // Получаем выбор пользователя | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. По логике мы получаем выбор пользователя а по коду начинаем валидацию. Неправильно назван метод и класс |
||
|
|
||
| switch (num) { | ||
| case 0: | ||
| System.out.println("Выход из программы. Пока!"); | ||
| return; | ||
|
|
||
| case 1: | ||
| encryptFile(); // Шифруем файл | ||
| System.out.println("Готово!"); | ||
| break; | ||
|
|
||
| case 2: | ||
| decryptFile(); // Расшифровываем файл по ключу | ||
| System.out.println("Готово!"); | ||
| break; | ||
|
|
||
| case 3: | ||
| bruteForceFile(); // Расшифровка методом полного перебора | ||
| System.out.println("Готово!"); | ||
| break; | ||
|
|
||
| case 4: | ||
| statisticalFile(); // Расшифровка статистическим анализом | ||
| System.out.println("Готово!"); | ||
| break; | ||
|
|
||
| default: | ||
| System.out.println("Некорректный выбор, попробуйте снова."); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Метод для шифрования текста из файла | ||
| private static void encryptFile() throws IOException { | ||
| int key = Validator.scanKey(); // Считываем ключ от пользователя | ||
|
|
||
| // Читаем текст из файла | ||
| ArrayList<String> text = new ArrayList<>(Files.readAllLines(Path.of("text\\text.txt"))); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Такой путь будет работать только в Windows. |
||
|
|
||
| // Создаём новый файл для зашифрованного текста | ||
| FileService.createNewFile("text\\encrypted.txt"); | ||
|
|
||
| // Шифруем текст с указанным ключом | ||
| Encrypt encrypt = new Encrypt(); | ||
| ArrayList<String> result = new ArrayList<>(encrypt.encrypt(text, key)); | ||
|
|
||
| // Записываем результат в файл | ||
| FileService.writeFile("text\\encrypted.txt", result); | ||
| } | ||
|
|
||
| // Метод для расшифровки текста по ключу | ||
| private static void decryptFile() throws IOException { | ||
| int key = Validator.scanKey(); // Считываем ключ от пользователя | ||
|
|
||
| // Читаем зашифрованный текст | ||
| ArrayList<String> text = new ArrayList<>(Files.readAllLines(Path.of("text\\encrypted.txt"))); | ||
|
|
||
| // Создаём файл для расшифрованного текста | ||
| FileService.createNewFile("text\\decrypt.txt"); | ||
|
|
||
| // Расшифровываем текст | ||
| Decrypt decrypt = new Decrypt(); | ||
| FileService.writeFile("text\\decrypt.txt", decrypt.decrypt(text, key)); | ||
| } | ||
|
|
||
| // Метод для расшифровки текста методом полного перебора (Brute Force) | ||
| private static void bruteForceFile() throws IOException { | ||
| ArrayList<String> text = new ArrayList<>(Files.readAllLines(Path.of("text\\encrypted.txt"))); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. тут для всех файлов жёстко заданный пути и имена, в реальных программах так никогда не делают, пользователь должен иметь возможность указать те пути которые нужны ему |
||
|
|
||
| // Создаём файл для результата | ||
| FileService.createNewFile("text\\bruteForce.txt"); | ||
|
|
||
| BruteForce bruteForce = new BruteForce(); | ||
|
|
||
| // Записываем расшифрованный текст с найденным ключом | ||
| FileService.writeFile("text\\bruteForce.txt", bruteForce.bruteForce(text)); | ||
| } | ||
|
|
||
| // Метод для расшифровки текста статистическим анализом | ||
| private static void statisticalFile() throws IOException { | ||
| ArrayList<String> text = new ArrayList<>(Files.readAllLines(Path.of("text\\encrypted.txt"))); | ||
|
|
||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. не очень понятно зачем нужны эти пустые строки |
||
| // Создаём файл для результата | ||
| FileService.createNewFile("text\\statisticalAnalysis.txt"); | ||
|
|
||
| StatisticalAnalysis statisticalAnalysis = new StatisticalAnalysis(); | ||
|
|
||
| // Записываем расшифрованный текст | ||
| FileService.writeFile("text\\statisticalAnalysis.txt", statisticalAnalysis.statisticalAnalysis(text)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.javarush.orlov; | ||
|
|
||
| import java.util.ArrayList; | ||
|
|
||
| public class Decrypt { | ||
|
|
||
| // Метод расшифровывает текст, используя ключ шифра Цезаря | ||
| // Для расшифровки используется метод шифрования с отрицательным ключом | ||
| public ArrayList<String> decrypt(ArrayList<String> text, int key) { | ||
|
|
||
| // Создаём объект класса Encrypt для работы с шифром Цезаря | ||
| Encrypt encrypt = new Encrypt(); | ||
|
|
||
| // Возвращаем результат шифрования с отрицательным ключом, | ||
| // что фактически выполняет расшифровку | ||
| return encrypt.encrypt(text, -key); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package com.javarush.orlov; | ||
|
|
||
| import java.util.ArrayList; | ||
|
|
||
| public class Encrypt { | ||
|
|
||
| // Алфавит, используемый для шифрования и дешифрования | ||
| // Содержит русские буквы в нижнем и верхнем регистре, знаки препинания и пробел | ||
| public static final char[] ALPHABET = {'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з', | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. по смыслу это константа, но технически это массив, а массивы можно изменять,можно конечно оставить и так, но если делать все совсем строго, то лучше дать этому полю обычное имя |
||
| 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', | ||
| 'ъ', 'ы', 'ь', 'э', 'ю', 'я', '.', ',', '«', '»', '"', '\'', ':', '!', '?', ' ', | ||
| 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', | ||
| 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я'}; | ||
|
|
||
| // Шифрует или расшифровывает текст с помощью ключа | ||
| // Если ключ положительный — шифрование, если отрицательный — расшифровка | ||
| public ArrayList<String> encrypt(ArrayList<String> text, int key) { | ||
|
|
||
| ArrayList<String> result = new ArrayList<>(); | ||
|
|
||
| // Обрабатываем каждую строку текста | ||
| for (String s : text) { | ||
| result.add(checkAlphabet(s, key)); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Проверяет каждый символ строки и сдвигает его по алфавиту | ||
| private String checkAlphabet(String text, int key) { | ||
| StringBuilder builder = new StringBuilder(); | ||
|
|
||
| // Перебираем символы строки | ||
| for (int k = 0; k < text.length(); k++) { | ||
| char current = text.charAt(k); | ||
|
|
||
| // Проверяем, есть ли символ в алфавите | ||
| boolean found = isFound(key, current, builder); | ||
|
|
||
| // Если символа нет в алфавите — оставляем его без изменений | ||
| if (!found) { | ||
| builder.append(current); | ||
| } | ||
| } | ||
|
|
||
| return builder.toString(); | ||
| } | ||
|
|
||
| // Сдвигает символ по алфавиту на заданное количество позиций | ||
| // Возвращает true, если символ найден в алфавите, иначе false | ||
| private boolean isFound(int key, char current, StringBuilder builder) { | ||
| for (int j = 0; j < ALPHABET.length; j++) { // перебор всех символов алфавита | ||
| if (current == ALPHABET[j]) { | ||
| // Сдвиг с учетом длины алфавита и обработки отрицательного ключа | ||
| builder.append(ALPHABET[(j + key % ALPHABET.length + ALPHABET.length) % ALPHABET.length]); | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package com.javarush.orlov; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.ArrayList; | ||
|
|
||
| public class FileService { | ||
|
|
||
| // Метод для чтения содержимого файла в виде строки | ||
| public static String readFile(String filePath) throws IOException { | ||
| Path path = Paths.get(filePath); // получаем путь к файлу | ||
| byte[] bytes = Files.readAllBytes(path); // читаем все байты из файла | ||
| return new String(bytes, StandardCharsets.UTF_8); // преобразуем байты в строку с кодировкой UTF-8 | ||
| } | ||
|
|
||
| // Метод для создания нового файла | ||
| // Если файл уже существует — удаляет его и создаёт новый | ||
| public static void createNewFile(String filePath) throws IOException { | ||
| Path path = Path.of(filePath); // путь к файлу | ||
|
|
||
| if (Files.exists(path)) { // проверяем, существует ли файл | ||
| Files.delete(path); // если да — удаляем | ||
| } | ||
| Files.createFile(path); // создаём новый файл | ||
| } | ||
|
|
||
| // Метод для записи текста в файл | ||
| public static void writeFile(String filePath, ArrayList<String> str) { | ||
| Path path = Paths.get(filePath); // путь к файлу | ||
| try { | ||
| StringBuilder builder = new StringBuilder(); | ||
|
|
||
| // Объединяем все строки с переносами | ||
| for (String line : str) { | ||
| builder.append(line).append(System.lineSeparator()); | ||
| } | ||
|
|
||
| // Запись в файл с явным указанием кодировки UTF-8 | ||
| Files.writeString(path, builder.toString(), StandardCharsets.UTF_8); | ||
|
|
||
| } catch (IOException e) { | ||
| // Вывод ошибки, если запись не удалась | ||
| System.out.println("Ошибка записи файла: " + e.getMessage()); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| package com.javarush.orlov; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| public class StatisticalAnalysis { | ||
|
|
||
| // Выполняет статистический анализ текста для взлома шифра Цезаря | ||
| // Объединяет весь текст в одну строку, находит самый частый символ и расшифровывает текст | ||
| public ArrayList<String> statisticalAnalysis(ArrayList<String> text) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. метод полностью дублирует название класса |
||
| ArrayList<String> result = new ArrayList<>(); | ||
|
|
||
| // Объединяем все строки текста в одну для анализа частоты символов | ||
| StringBuilder allText = new StringBuilder(); | ||
| for (String line : text) { | ||
| allText.append(line); | ||
| } | ||
|
|
||
| // Находим самый часто встречающийся символ во всём тексте | ||
| char top = word(allText.toString()); | ||
|
|
||
| // Вычисляем ключ шифра, предполагая, что самый частый символ — пробел | ||
| int key = differenceWithSpace(top); | ||
|
|
||
| // Расшифровываем текст с найденным ключом | ||
| Decrypt decrypt = new Decrypt(); | ||
| result = decrypt.decrypt(text, key); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Находит символ, который встречается чаще всего в строке | ||
| private char word(String line) { | ||
| HashMap<Character, Integer> count = new HashMap<>(); | ||
|
|
||
| // Подсчитываем количество каждого символа | ||
| for (int j = 0; j < line.length(); j++) { | ||
| char c = line.charAt(j); | ||
| count.put(c, count.getOrDefault(c, 0) + 1); | ||
| } | ||
|
|
||
| char mostFrequent = ' '; | ||
| int maxCount = 0; | ||
|
|
||
| // Определяем символ с максимальной частотой | ||
| for (Map.Entry<Character, Integer> entry : count.entrySet()) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут основная логическая ошибка. Под статическим анализом подразумевается возможность что напротив каждой буквы стоит какая-то другая и они не имеют никакой упорядоченности друг относительно друга. Поэтому вычисления самого часто символа позволяет определить корректно только его, а весь остальной текст не расшифруется |
||
| if (entry.getValue() > maxCount) { | ||
| maxCount = entry.getValue(); | ||
| mostFrequent = entry.getKey(); | ||
| } | ||
| } | ||
|
|
||
| return mostFrequent; | ||
| } | ||
|
|
||
| // Вычисляет ключ шифра Цезаря как разницу позиции самого частого символа и пробела | ||
| // Если ключ отрицательный, корректирует его, добавляя длину алфавита | ||
| public static int differenceWithSpace(char mostFrequent) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Напишу здесь с другими словами. Статический анализ расшифровывает не именно шифр цезаря а потенциально любой шифр где одной букве соответствует какая-то другая. При этой реализации по сути получился вариант грубого перебора который уже и так реализован |
||
| int indexMostFrequent = -1; | ||
| int indexSpace = -1; | ||
|
|
||
| // Находим позиции символов в алфавите | ||
| for (int i = 0; i < Encrypt.ALPHABET.length; i++) { | ||
| if (Encrypt.ALPHABET[i] == mostFrequent) { | ||
| indexMostFrequent = i; | ||
| } | ||
| if (Encrypt.ALPHABET[i] == ' ') { | ||
| indexSpace = i; | ||
| } | ||
| } | ||
|
|
||
| // Если символы не найдены, выбрасываем исключение | ||
| if (indexMostFrequent == -1 || indexSpace == -1) { | ||
| throw new IllegalArgumentException("Символ не найден в алфавите"); | ||
| } | ||
|
|
||
| // Вычисляем ключ | ||
| int key = indexMostFrequent - indexSpace; | ||
| if (key < 0) key += Encrypt.ALPHABET.length; // корректировка отрицательного ключа | ||
|
|
||
| return key; | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Комментарии это хорошо, но во-первых лучше бы они были на английском языке, а во-вторых в Java активно используется Javadoc