Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/main/java/com/javarush/orlov/BruteForce.java
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 = {"и", "в", "на", "что", "как", "это", "он", "она", "они"};

// Метод перебирает все возможные ключи шифра Цезаря и выбирает лучший
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Комментарии это хорошо, но во-первых лучше бы они были на английском языке, а во-вторых в Java активно используется Javadoc

// Лучший ключ определяется по количеству слов из словаря, найденных в тексте
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;
}
}
105 changes: 105 additions & 0 deletions src/main/java/com/javarush/orlov/CaesarCipherApp.java
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(); // Получаем выбор пользователя
Copy link
Owner

Choose a reason for hiding this comment

The 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")));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Такой путь будет работать только в Windows.
Тут лучше Path.of("text","text.txt")


// Создаём новый файл для зашифрованного текста
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")));
Copy link
Owner

Choose a reason for hiding this comment

The 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")));

Copy link
Owner

Choose a reason for hiding this comment

The 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));
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/javarush/orlov/Decrypt.java
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);
}
}
62 changes: 62 additions & 0 deletions src/main/java/com/javarush/orlov/Encrypt.java
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 = {'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з',
Copy link
Owner

Choose a reason for hiding this comment

The 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;
}

}
49 changes: 49 additions & 0 deletions src/main/java/com/javarush/orlov/FileService.java
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());
}
}
}
85 changes: 85 additions & 0 deletions src/main/java/com/javarush/orlov/StatisticalAnalysis.java
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) {
Copy link
Owner

Choose a reason for hiding this comment

The 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()) {
Copy link
Owner

Choose a reason for hiding this comment

The 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) {
Copy link
Owner

Choose a reason for hiding this comment

The 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;
}

}
Loading