diff --git a/pom.xml b/pom.xml index a519ba9..f750ea4 100644 --- a/pom.xml +++ b/pom.xml @@ -36,5 +36,15 @@ commons-math3 3.6.1 + + junit + junit + test + + + org.junit.jupiter + junit-jupiter + test + \ No newline at end of file diff --git a/src/main/java/com/javarush/bakhtin/Application.java b/src/main/java/com/javarush/bakhtin/Application.java new file mode 100644 index 0000000..c0e0040 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/Application.java @@ -0,0 +1,7 @@ +package com.javarush.bakhtin; + +public class Application { + public static void main(String[] args) { + new Console().run(); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/bakhtin/Caesar.java b/src/main/java/com/javarush/bakhtin/Caesar.java new file mode 100644 index 0000000..e9fad31 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/Caesar.java @@ -0,0 +1,34 @@ +package com.javarush.bakhtin; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Path; + +public class Caesar { + + private final int FIRST_UNICODE_LETTER = 9; + private final int LAST_UNICODE_LETTER = 1104; + private final int NUM_OF_UNICODE_LETTERS = LAST_UNICODE_LETTER - FIRST_UNICODE_LETTER; + + public void decode(int key, Path from, Path to) throws IOException { + encode(-key, from, to); + } + + public void encode(int key, Path from, Path to) throws IOException { + try (FileReader reader = FileUtil.toFileReader(from); + FileWriter writer = FileUtil.toFileWriter(to)) { + while (reader.ready()) { + int inputUnicodeCode = reader.read(); + char resultChar = encodeChar(key, inputUnicodeCode); + writer.write(resultChar); + } + } + } + + protected char encodeChar(int key, int unicodeCode) { + return (char) ((unicodeCode - FIRST_UNICODE_LETTER + key % NUM_OF_UNICODE_LETTERS + NUM_OF_UNICODE_LETTERS) + % NUM_OF_UNICODE_LETTERS + FIRST_UNICODE_LETTER); // Общая формула для циклического сдвига + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/bakhtin/Console.java b/src/main/java/com/javarush/bakhtin/Console.java new file mode 100644 index 0000000..391486a --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/Console.java @@ -0,0 +1,22 @@ +package com.javarush.bakhtin; + +import java.util.InputMismatchException; + +public class Console { + + private final MenuController menuController = new MenuController(); + + protected void run() { + while (true) { + menuController.printCommands(); + try { + int answer = menuController.getUserCommand(); + menuController.executeCommand(answer); + } catch (InputMismatchException e) { + System.err.println("Ошибка, введите число"); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + } + } +} diff --git a/src/main/java/com/javarush/bakhtin/FileUtil.java b/src/main/java/com/javarush/bakhtin/FileUtil.java new file mode 100644 index 0000000..6ad1305 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/FileUtil.java @@ -0,0 +1,31 @@ +package com.javarush.bakhtin; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class FileUtil { + + public static FileWriter toFileWriter(Path toPath) throws IOException { + try { + if (!Files.exists(toPath)) { + Files.createFile(toPath); + } + return new FileWriter(toPath.toFile()); + } catch (FileNotFoundException e) { + throw new RuntimeException("Ошибка, файл не найден"); + } + } + + public static FileReader toFileReader(Path fromPath){ + try { + return new FileReader(fromPath.toFile()); + } catch (FileNotFoundException e) { + throw new RuntimeException("Ошибка, файл не найден"); + } + } + +} diff --git a/src/main/java/com/javarush/bakhtin/MenuController.java b/src/main/java/com/javarush/bakhtin/MenuController.java new file mode 100644 index 0000000..6c71781 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/MenuController.java @@ -0,0 +1,43 @@ +package com.javarush.bakhtin; + +import com.javarush.bakhtin.command.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Scanner; + +public class MenuController { + + private static final Scanner consoleInput = new Scanner(System.in); + + private static final String MENU_MESSAGE = "Введите цифру, соответствующую команде:"; + private static final HashMap menuItemMap = new HashMap<>(3); + + protected MenuController() { + menuItemMap.put(1, new EncoderCommand()); + menuItemMap.put(2, new DecoderCommand()); + menuItemMap.put(3, new BruteForce()); + menuItemMap.put(4, new ExitCommand()); + } + + protected void executeCommand(int answer) throws IOException { + menuItemMap.get(answer).execute(); + } + + protected void printCommands() { + System.out.println(MENU_MESSAGE); + for (var menuItem : MenuController.menuItemMap.entrySet()) { + System.out.print(menuItem.getValue().getCommandName()); + System.out.print(" - "); + System.out.println(menuItem.getKey()); + } + } + + public int getUserCommand() { + int answer = Integer.parseInt(consoleInput.nextLine()); + if (answer < 1 || answer > 4) { + throw new RuntimeException("Ошибка, введите число от 1 до 4"); + } + return answer; + } +} diff --git a/src/main/java/com/javarush/bakhtin/command/BruteForce.java b/src/main/java/com/javarush/bakhtin/command/BruteForce.java new file mode 100644 index 0000000..25539c9 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/command/BruteForce.java @@ -0,0 +1,72 @@ +package com.javarush.bakhtin.command; + +import com.javarush.bakhtin.Caesar; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BruteForce implements MenuCommand { + + private static final String COMMAND_NAME = "Брутфорс"; + + public String getCommandName() { + return COMMAND_NAME; + } + + private static final Pattern WORD_PATTERN = Pattern.compile("[а-я]{4,20}"); + + private Set wordSet = new HashSet<>(); + private Set encodedWordSet = new HashSet<>(); + + private final CaesarParamReader caesarParamReader = new CaesarParamReader(); + private final Caesar caesar = new Caesar(); + + public void makeAWordListFromDictionary() throws IOException { + String dict = Files.readString(caesarParamReader.getDictFromUser()); + Matcher matcher = WORD_PATTERN.matcher(dict); + while (matcher.find()) { + wordSet.add(matcher.group()); + } + } + + private void makeAWordListFromDecodedFile(Path path) throws IOException { + encodedWordSet.clear(); + String text = Files.readString(path); + Matcher matcher = WORD_PATTERN.matcher(text); + while (matcher.find()) { + encodedWordSet.add(matcher.group()); + } + } + + public void execute() throws IOException { + makeAWordListFromDictionary(); + int maxNumOfMatches = 0; + int shiftOfMaxNumOfMatches = 0; + Path encodedPath = caesarParamReader.getEncodedFromUser(); + Path decodedPath = caesarParamReader.getDecodedFromUser(); + + for (int shift = -50; shift <= 50; shift++) { + System.out.println("Старт " + shift); + caesar.decode(shift, encodedPath, decodedPath); + makeAWordListFromDecodedFile(decodedPath); + int findCounter = 0; + for (String string : encodedWordSet) { + if (wordSet.contains(string)) { + findCounter++; + } + } + System.out.println("Стоп: найдено слов:" + findCounter); + if (findCounter > maxNumOfMatches) { + maxNumOfMatches = findCounter; + shiftOfMaxNumOfMatches = shift; + } + } + System.out.println("Правильный ключ: " + shiftOfMaxNumOfMatches); + caesar.decode(shiftOfMaxNumOfMatches, encodedPath, decodedPath); + System.out.println("Брутфорс завершен"); + } +} diff --git a/src/main/java/com/javarush/bakhtin/command/CaesarParamReader.java b/src/main/java/com/javarush/bakhtin/command/CaesarParamReader.java new file mode 100644 index 0000000..5dda146 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/command/CaesarParamReader.java @@ -0,0 +1,68 @@ +package com.javarush.bakhtin.command; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Scanner; + +public class CaesarParamReader { + + private static final String KEY_INPUT_MESSAGE = "Введите ключ шифрования, по умолчанию - 1:"; + private static final String FROM_PATH_INPUT_MESSAGE = "Введите путь, откуда вы хотите считать данные или нажмите enter, чтобы выбрать "; + private static final String DICT_PATH_INPUT_MESSAGE = "Введите путь к словарю или нажмите enter, чтобы выбрать "; + private static final String ENCODED_PATH_INPUT_MESSAGE = "Введите путь к зашифрованному файлу или нажмите enter, чтобы выбрать "; + private static final String DECODED_PATH_INPUT_MESSAGE = "Введите путь к расшифрованному файлу или нажмите enter, чтобы выбрать "; + private static final Path DEFAULT_DICT_PATH = Path.of("text", "dict.txt"); + private static final Path DEFAULT_ENCODED_PATH = Path.of("text", "encrypted.txt"); + private static final Path DEFAULT_DECODED_PATH = Paths.get("text", "decrypted.txt"); + + private final Scanner consoleScanner = new Scanner(System.in); + + private static int key = 1; + private static final CaesarParamReader instance = new CaesarParamReader(); + + public static CaesarParamReader getInstance() { + return instance; + } + + private Path getPath(Path defaultPath) { + String consoleString = consoleScanner.nextLine(); + if (!consoleString.isEmpty()) { + return Path.of(consoleString); + } + return defaultPath; + } + + protected int getKeyFromUser() { + System.out.print(KEY_INPUT_MESSAGE); + try { + int inputKey = Integer.parseInt(consoleScanner.nextLine()); + if (inputKey != 0) { + key = inputKey; + } + return key; + } catch (NumberFormatException e) { + throw new RuntimeException("Ошибка, введите число"); + } + } + + protected Path getFromPathFromUser(Path defaultFrom) { + System.out.println(FROM_PATH_INPUT_MESSAGE + defaultFrom.getFileName()); + return getPath(defaultFrom); + } + + protected Path getDictFromUser() { + System.out.println(DICT_PATH_INPUT_MESSAGE + DEFAULT_DICT_PATH.getFileName()); + return getPath(DEFAULT_DICT_PATH); + } + + protected Path getEncodedFromUser() { + System.out.println(ENCODED_PATH_INPUT_MESSAGE + DEFAULT_ENCODED_PATH.getFileName()); + return getPath(DEFAULT_ENCODED_PATH); + } + + protected Path getDecodedFromUser() { + System.out.println(DECODED_PATH_INPUT_MESSAGE + DEFAULT_DECODED_PATH.getFileName()); + return getPath(DEFAULT_DECODED_PATH); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/bakhtin/command/DecoderCommand.java b/src/main/java/com/javarush/bakhtin/command/DecoderCommand.java new file mode 100644 index 0000000..7d29366 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/command/DecoderCommand.java @@ -0,0 +1,24 @@ +package com.javarush.bakhtin.command; + +import com.javarush.bakhtin.Caesar; + +import java.io.IOException; +import java.nio.file.Path; + +public class DecoderCommand implements MenuCommand { + + private static final String COMMAND_NAME = "Расшифровать"; + + public String getCommandName() { + return COMMAND_NAME; + } + + public void execute() throws IOException { + var reader = CaesarParamReader.getInstance(); + int key = reader.getKeyFromUser(); + Path from = reader.getEncodedFromUser(); + Path to = reader.getDecodedFromUser(); + new Caesar().decode(key, from, to); + } + +} diff --git a/src/main/java/com/javarush/bakhtin/command/EncoderCommand.java b/src/main/java/com/javarush/bakhtin/command/EncoderCommand.java new file mode 100644 index 0000000..d0b2db1 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/command/EncoderCommand.java @@ -0,0 +1,27 @@ +package com.javarush.bakhtin.command; + +import com.javarush.bakhtin.Caesar; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class EncoderCommand implements MenuCommand { + + private static final Path DEFAULT_TEXT_PATH = Paths.get("text", "text.txt"); + private static final String COMMAND_NAME = "Зашифровать"; + + public String getCommandName() { + return COMMAND_NAME; + } + + public void execute() throws IOException { + var reader = CaesarParamReader.getInstance(); + int key = reader.getKeyFromUser(); + Path from = reader.getFromPathFromUser(DEFAULT_TEXT_PATH); + Path to = reader.getEncodedFromUser(); + + new Caesar().encode(key, from, to); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/bakhtin/command/ExitCommand.java b/src/main/java/com/javarush/bakhtin/command/ExitCommand.java new file mode 100644 index 0000000..47ae5bf --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/command/ExitCommand.java @@ -0,0 +1,15 @@ +package com.javarush.bakhtin.command; + +public class ExitCommand implements MenuCommand { + + private static final String COMMAND_NAME = "Выход из программы"; + + public String getCommandName() { + return COMMAND_NAME; + } + + public void execute() { + System.out.print("Выход из программы..."); + System.exit(0); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/bakhtin/command/MenuCommand.java b/src/main/java/com/javarush/bakhtin/command/MenuCommand.java new file mode 100644 index 0000000..d997b69 --- /dev/null +++ b/src/main/java/com/javarush/bakhtin/command/MenuCommand.java @@ -0,0 +1,10 @@ +package com.javarush.bakhtin.command; + +import java.io.IOException; + +public interface MenuCommand { + + String getCommandName(); + + void execute() throws IOException; +}