-
Notifications
You must be signed in to change notification settings - Fork 47
Костромин - 3 tasks #27
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?
Changes from all commits
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,9 @@ | ||
| package com.javarush.kostromin; | ||
|
|
||
| import com.javarush.kostromin.view.console.ConsoleApplication; | ||
|
|
||
| public class ConsoleRunner { | ||
| public static void main(String[] args) { | ||
| new ConsoleApplication().run(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.javarush.kostromin.command; | ||
|
|
||
| import static com.javarush.kostromin.constant.Alphabet.ALPHABET; | ||
|
|
||
| public abstract class BaseCipher { | ||
|
|
||
| protected String processText(String text, int shift) { | ||
| StringBuilder result = new StringBuilder(); | ||
|
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 n = ALPHABET.length(); | ||
| shift = ((shift % n) + n) % n; | ||
|
|
||
| for (char c : text.toCharArray()) { | ||
| int index = ALPHABET.indexOf(c); | ||
| if (index != -1) { | ||
| int newIndex = (index + shift) % n; | ||
| result.append(ALPHABET.charAt(newIndex)); | ||
| } else { | ||
| result.append(c); | ||
| } | ||
| } | ||
| return result.toString(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package com.javarush.kostromin.command; | ||
| import com.javarush.kostromin.constant.Alphabet; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| public class BruteForceMethod { | ||
| private static final List<String> COMMON_WORDS = Arrays.asList( | ||
|
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 int findKey(String encryptedText) { | ||
| DecryptMethodCaesar decryptor = new DecryptMethodCaesar(); | ||
| int maxMatches = -1; | ||
| int bestKey = 0; | ||
| int alphabetLength = Alphabet.ALPHABET.length(); | ||
|
|
||
| for (int key = 1; key < alphabetLength; key++) { | ||
| String decrypted = decryptor.decrypt(encryptedText, key); | ||
| int matches = countCommonWords(decrypted); | ||
|
|
||
| if (matches > maxMatches) { | ||
| maxMatches = matches; | ||
| bestKey = key; | ||
| } | ||
| } | ||
| return bestKey; | ||
| } | ||
|
|
||
| private int countCommonWords(String text) { | ||
| String lowerText = text.toLowerCase(); | ||
| int count = 0; | ||
| for (String word : COMMON_WORDS) { | ||
| if (lowerText.contains(word)) { | ||
| count++; | ||
| } | ||
| } | ||
| return count; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.javarush.kostromin.command; | ||
|
|
||
| public class DecryptMethodCaesar extends BaseCipher { | ||
| public String decrypt(String text, int key) { | ||
| return processText(text, -key); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.javarush.kostromin.command; | ||
|
|
||
| public class EncryptMethodCaesar extends BaseCipher { | ||
| public String encrypt(String text, int key) { | ||
| return processText(text, key); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.javarush.kostromin.constant; | ||
|
|
||
| public class Alphabet { | ||
|
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 static final String ALPHABET = | ||
| "абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ0123456789 .,!?:;-()"; | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.javarush.kostromin.constant; | ||
|
|
||
| import java.io.File; | ||
|
|
||
| public class Constants { | ||
| public static final String TXT_FOLDER = System.getProperty("user.dir") + | ||
|
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. То же самое |
||
| File.separator + | ||
| "text" + | ||
| File.separator; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| package com.javarush.kostromin.controller; | ||
|
|
||
| import com.javarush.kostromin.command.BruteForceMethod; | ||
| import com.javarush.kostromin.command.DecryptMethodCaesar; | ||
| import com.javarush.kostromin.command.EncryptMethodCaesar; | ||
| import com.javarush.kostromin.constant.Alphabet; | ||
|
|
||
| import java.io.File; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Paths; | ||
|
|
||
| public class MainController { | ||
| public void executeCommand(String command, String[] args) { | ||
| try { | ||
| switch (command) { | ||
| case "encrypt": | ||
|
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. В таких случаях полезно выносить ключи в константы |
||
| validateFileExists(args[0]); | ||
| validateKey(args[2]); | ||
|
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. Для аргументов тоже стоило бы выделить переменные с понятными именами |
||
| encryptFile(args[0], args[1], Integer.parseInt(args[2])); | ||
| break; | ||
| case "decrypt": | ||
| validateFileExists(args[0]); | ||
| validateKey(args[2]); | ||
| decryptFile(args[0], args[1], Integer.parseInt(args[2])); | ||
| break; | ||
| case "bruteforce": | ||
| validateFileExists(args[0]); | ||
| bruteForceFile(args[0], args[1]); | ||
| break; | ||
| default: | ||
| System.out.println("Unknown command: " + command); | ||
|
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. Ввод и вывод лучше всего выполнять на уровне взаимодействия с пользователем а не логических конструкциях приложения, это позволяет легко менять тип приложения |
||
| } | ||
| } catch (RuntimeException e) { | ||
| System.out.println("Error: " + e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void validateFileExists(String filePath) { | ||
| if (!new File(filePath).exists()) { | ||
| throw new IllegalArgumentException("File does not exist: " + filePath); | ||
| } | ||
| } | ||
|
|
||
| private void validateKey(String keyFromKeyboard) { | ||
| try { | ||
| int key = Integer.parseInt(keyFromKeyboard); | ||
| int alphabetLength = Alphabet.ALPHABET.length(); | ||
| if (key < 0 || key >= alphabetLength) { | ||
| throw new IllegalArgumentException("Key must be between 0 and " + | ||
| (alphabetLength - 1)); | ||
| } | ||
| } catch (NumberFormatException e) { | ||
| throw new IllegalArgumentException("Key must be a valid integer"); | ||
| } | ||
| } | ||
|
|
||
| private void encryptFile(String sourceFile, String destFile, int key) { | ||
| try { | ||
| String contentText = new String(Files.readAllBytes(Paths.get(sourceFile))); | ||
| EncryptMethodCaesar encryptor = new EncryptMethodCaesar(); | ||
| String encryptedContent = encryptor.encrypt(contentText, key); | ||
| Files.write(Paths.get(destFile), encryptedContent.getBytes()); | ||
| System.out.println("File encrypted successfully with key: " + key); | ||
|
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. Тоже самое, тут лучше не выводить явно на консоль а вернуть в виде какого-то результата |
||
| } catch (Exception e) { | ||
| throw new RuntimeException("Encryption failed: " + e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void decryptFile(String sourceFile, String destFile, int key) { | ||
| try { | ||
| String contentText = new String(Files.readAllBytes(Paths.get(sourceFile))); | ||
| DecryptMethodCaesar decryptor = new DecryptMethodCaesar(); | ||
| String decryptedContent = decryptor.decrypt(contentText, key); | ||
| Files.write(Paths.get(destFile), decryptedContent.getBytes()); | ||
| System.out.println("File decrypted successfully with key: " + key); | ||
| } catch (Exception e) { | ||
| throw new RuntimeException("Decryption failed: " + e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void bruteForceFile(String sourceFile, String destFile) { | ||
| try { | ||
| String contentText = new String(Files.readAllBytes(Paths.get(sourceFile))); | ||
| BruteForceMethod bruteForce = new BruteForceMethod(); | ||
| int foundKey = bruteForce.findKey(contentText); | ||
|
|
||
| DecryptMethodCaesar decryptor = new DecryptMethodCaesar(); | ||
| String decryptedContent = decryptor.decrypt(contentText, foundKey); | ||
| Files.write(Paths.get(destFile), decryptedContent.getBytes()); | ||
|
|
||
| System.out.println("Brute force completed. Found key: " + foundKey); | ||
| } catch (Exception e) { | ||
| throw new RuntimeException("Brute force failed: " + e.getMessage()); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| package com.javarush.kostromin.view.console; | ||
|
|
||
| import com.javarush.kostromin.constant.Alphabet; | ||
| import com.javarush.kostromin.controller.MainController; | ||
|
|
||
| import java.io.File; | ||
| import java.util.Scanner; | ||
|
|
||
| import static com.javarush.kostromin.constant.Constants.TXT_FOLDER; | ||
|
|
||
| public class ConsoleApplication { | ||
| private final MainController controller = new MainController(); | ||
|
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 void run() { | ||
| Scanner scanner = new Scanner(System.in); | ||
| while (true) { | ||
| printMenu(); | ||
| String choice = scanner.nextLine(); | ||
| System.out.println(TXT_FOLDER); | ||
| switch (choice) { | ||
| case "1": | ||
| executeEncryptCommand(scanner); | ||
| break; | ||
| case "2": | ||
| executeDecryptCommand(scanner); | ||
| break; | ||
| case "3": | ||
| executeBruteForceCommand(scanner); | ||
| break; | ||
| case "4": | ||
| return; | ||
| default: | ||
| System.out.println("Invalid option"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private void executeEncryptCommand(Scanner scanner) { | ||
| System.out.println("Enter input file [Push 'Enter' for default: text/text.txt]:"); | ||
|
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. Да именно на этом уровне ввод вывод наиболее уместен |
||
| String inputFile = getInputFilePath(scanner, "text.txt"); | ||
|
|
||
| System.out.println("Enter output file [Push 'Enter' for for default: text/encrypted.txt]:"); | ||
| String outputFile = getOutputFilePath(scanner, "encrypted.txt"); | ||
|
|
||
| System.out.println("Enter key:"); | ||
| int key = getValidKey(scanner); | ||
|
|
||
| controller.executeCommand("encrypt", new String[]{inputFile, outputFile, String.valueOf(key)}); | ||
| } | ||
|
|
||
| private void executeDecryptCommand(Scanner scanner) { | ||
| System.out.println("Enter input file [Push 'Enter' for default: text/encrypted.txt]:"); | ||
| String inputFile = getInputFilePath(scanner, "encrypted.txt"); | ||
|
|
||
| System.out.println("Enter output file [Push 'Enter' for default: text/decrypted.txt]:"); | ||
| String outputFile = getOutputFilePath(scanner, "decrypted.txt"); | ||
|
|
||
| System.out.println("Enter key:"); | ||
| int key = getValidKey(scanner); | ||
|
|
||
| controller.executeCommand("decrypt", new String[]{inputFile, outputFile, String.valueOf(key)}); | ||
| } | ||
|
|
||
| private void executeBruteForceCommand(Scanner scanner) { | ||
| System.out.println("Enter input file [Push 'Enter' for default: text/encrypted.txt]:"); | ||
| String inputFile = getInputFilePath(scanner, "encrypted.txt"); | ||
|
|
||
| System.out.println("Enter output file [Push 'Enter' for default: text/decrypted_bruteforce.txt]:"); | ||
| String outputFile = getOutputFilePath(scanner, "decrypted_bruteforce.txt"); | ||
|
|
||
| controller.executeCommand("bruteforce", new String[]{inputFile, outputFile}); | ||
| } | ||
|
|
||
| private String getInputFilePath(Scanner scanner, String defaultPath) { | ||
| String userInputPath = scanner.nextLine().trim(); | ||
| if (userInputPath.isEmpty()) { | ||
| userInputPath = TXT_FOLDER + defaultPath; | ||
| } | ||
|
|
||
| if (!new File(userInputPath).exists()) { | ||
| System.out.println("File does not exist: " + userInputPath); | ||
| System.out.println("Please enter a valid file path:"); | ||
| return getInputFilePath(scanner, defaultPath); | ||
| } | ||
|
|
||
| return userInputPath; | ||
| } | ||
|
|
||
| private String getOutputFilePath(Scanner scanner, String defaultPath) { | ||
| String userInputPath = scanner.nextLine().trim(); | ||
| if (userInputPath.isEmpty()) { | ||
| return TXT_FOLDER + defaultPath; | ||
| } else { | ||
| String pathWithTxt = ensureTxtExtension(userInputPath); | ||
| if (!pathWithTxt.contains(File.separator) && !pathWithTxt.contains("/") && !pathWithTxt.contains("\\")) { | ||
| return TXT_FOLDER + pathWithTxt; | ||
| } | ||
|
|
||
| return pathWithTxt; | ||
| } | ||
| } | ||
|
|
||
| private String ensureTxtExtension(String filePath) { | ||
| if (filePath.endsWith(File.separator)) { | ||
| return filePath + "output.txt"; | ||
| } | ||
| if (!filePath.toLowerCase().endsWith(".txt")) { | ||
| if (filePath.contains(".")) { | ||
| int lastDotIndex = filePath.lastIndexOf('.'); | ||
| filePath = filePath.substring(0, lastDotIndex) + ".txt"; | ||
| } else { | ||
| filePath += ".txt"; | ||
| } | ||
| } | ||
|
|
||
| return filePath; | ||
| } | ||
|
|
||
| private int getValidKey(Scanner scanner) { | ||
| while (true) { | ||
| try { | ||
| String userInput = scanner.nextLine().trim(); | ||
| int key = Integer.parseInt(userInput); | ||
|
|
||
| if (key < 0 || key >= Alphabet.ALPHABET.length()) { | ||
| System.out.println("Key must be between 0 and " + | ||
| (Alphabet.ALPHABET.length() - 1)); | ||
| continue; | ||
| } | ||
|
|
||
| return key; | ||
| } catch (NumberFormatException e) { | ||
| System.out.println("Please enter a valid integer key:"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void printMenu() { | ||
|
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. Класс получился достаточно большим в принципе можно разбить его на функциональные фрагменты |
||
| System.out.println("\n Caesar Cipher Application "); | ||
| System.out.println("-----------------------------"); | ||
| System.out.println("|1. Encrypt"); | ||
| System.out.println("|2. Decrypt with key"); | ||
| System.out.println("|3. Brute force"); | ||
| System.out.println("|4. Exit"); | ||
| System.out.println("-----------------------------"); | ||
| System.out.println("|Choose an option: "); | ||
| System.out.println("-----------------------------"); | ||
| } | ||
| } | ||
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.
Обычно при сборке приложения через конструктор передают все необходимые зависимости