-
Notifications
You must be signed in to change notification settings - Fork 47
Эмирзаков - 3 tasks (С) #40
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
b0cfb6a
2ebabc6
7994e42
66ce5c2
921e585
913c713
0abdd64
67e356f
64ab11b
594b73d
451fbdb
e284934
883584a
5e42d9f
846a2f9
85d4ae2
c6c1c4a
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,73 @@ | ||
| When Al Templeton called me on the seventh of June 2011, and told me to come down to the diner, he sounded like a man who was gargling with nails. | ||
| He also sounded like he was about to cry. | ||
| Since Al was the toughest man I had ever known (my old man included), this was scary. | ||
| "What's wrong?" I asked. | ||
| "Are you sick?" | ||
| "Just get here, Jake. | ||
| Please." | ||
| He hung up. | ||
| I went. | ||
| He’d given me a job when I needed one, washing dishes and helping him short-order. | ||
| I owed him. | ||
| Besides, I was worried. | ||
| I hadn't seen him in... | ||
| when? | ||
| Mid-May, I guess. | ||
| That was when he'd put the CLOSED FOR REPAIRS sign on the door of the fat-boy diner. | ||
| When I asked him what repairs, he’d been vague. | ||
| The sign was still on the door, but the door was unlocked. | ||
| I went in. | ||
| The place was dark and hot. | ||
| The shades were pulled. | ||
| "Al?" | ||
| "In the booth," he whispered. | ||
| I walked past the counter, and my footsteps echoed in the empty room. | ||
| He was in the back booth, the one we called the Office. | ||
| He was sitting in the shadows. | ||
| "Al, what's going on? | ||
| You look..." | ||
| I stopped. | ||
| Because he didn't look. | ||
| Not like Al. | ||
| The Al Templeton I knew was a big man, six-two and at least two-thirty, most of it gut. | ||
| He was hearty. | ||
| He was loud. | ||
| The man in the booth was a scarecrow. | ||
| His cheeks were hollows. | ||
| His eyes were huge and staring. | ||
| His hair, which had been graying, was now almost entirely white, and it was thin, wispy. | ||
| He was wearing one of his endless supply of flannel shirts, and it hung on him like a tent. | ||
| He must have lost sixty pounds. | ||
| "Jesus, Al," I said. | ||
| "What happened to you? | ||
| Cancer?" | ||
| He nodded. | ||
| "Lung. | ||
| Stage four. | ||
| They found it when I... | ||
| when I came back." | ||
| "Came back? | ||
| From where? | ||
| Florida? | ||
| I thought you were just closing for repairs." | ||
| "I lied," he said, and his voice was a dry rasp. | ||
| "I haven't been in Florida, Jake. | ||
| I've been in 1958." | ||
| I stared at him. | ||
| He stared back. | ||
| His eyes were perfectly steady. | ||
| He meant it. | ||
| "You're insane," I said. | ||
| "No. | ||
| I'm dying. | ||
| And I need you to listen to me. | ||
| I need you to listen to me very, very carefully. | ||
| Because I’m going to tell you something that will change your life. | ||
| And maybe change the world." | ||
| He pointed toward the pantry. | ||
| "It started in there. | ||
| In the back. | ||
| There's a... | ||
| a bubble. | ||
| A rabbit-hole. | ||
| And it goes to the past." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.javarush.emirzakov.io; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
|
|
||
| public class FileReaderService { | ||
|
|
||
| public String readFromFile(String path) { | ||
| try { | ||
| return Files.readString(Path.of(path)); | ||
| } catch (IOException e) { | ||
| System.out.println("Error reading file: " + e.getMessage()); | ||
| return ""; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.javarush.emirzakov.io; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
|
|
||
| public class FileWriterService { | ||
| public void writeFile(String path, CharSequence content) { | ||
| try { | ||
| Files.writeString(Path.of(path), content); | ||
| System.out.println("Files successfully written to: " + path); | ||
| } catch (IOException e) { | ||
| System.out.println("Error writing to file: " + e.getMessage()); | ||
|
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. Логирование через System.out.println в сервисах — плохая практика. Используйте SLF4J/Logback или верните статус выполнения. |
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| package com.javarush.emirzakov.main.java; | ||
|
|
||
|
|
||
| import com.javarush.emirzakov.io.FileReaderService; | ||
| import com.javarush.emirzakov.io.FileWriterService; | ||
| import com.javarush.emirzakov.service.BruteForceAction; | ||
| import com.javarush.emirzakov.service.DecryptAction; | ||
| import com.javarush.emirzakov.service.EncryptAction; | ||
| import com.javarush.emirzakov.service.TextUtils; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.Scanner; | ||
|
|
||
| public class Runner { | ||
| public static void main(String[] args) { | ||
|
|
||
| FileReaderService fileReader = new FileReaderService(); | ||
|
|
||
| try (Scanner scanner = new Scanner(System.in)) { | ||
| System.out.println("Enter text directly, or specify a file path: "); | ||
| String input = scanner.nextLine(); | ||
|
|
||
| String text; | ||
| Path path = Path.of(input); | ||
| if (Files.exists(path) && Files.isRegularFile(path)) { | ||
| System.out.println("File has been found and read!"); | ||
| text = fileReader.readFromFile(input); | ||
| } else { | ||
| text = input; | ||
|
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. Используйте try-with-resources для всех I/O операций. Хотя Scanner закрыт, логика работы с файлами разбросана. Инкапсулируйте проверку существования файла в сервис. |
||
| System.out.println("Using input text directly"); | ||
| } | ||
| String normalized = TextUtils.normalize(text); | ||
|
|
||
| System.out.println("Normalized text: "); | ||
| System.out.println(normalized.substring(0, Math.min(300, normalized.length())) + "..."); | ||
|
|
||
|
|
||
| System.out.println("\nChoose action: "); | ||
| System.out.println("1 - Encrypt"); | ||
| System.out.println("2 - Decrypt"); | ||
| System.out.println("3 - Brute Force (try to find key automatically)"); | ||
|
|
||
| int choice = readInt(scanner, "Enter choice (1-3): "); | ||
| String message = "Enter key (integer): "; | ||
| String result = null; | ||
|
|
||
| switch (choice) { | ||
| case 1 -> { | ||
| int key = readInt(scanner, message); | ||
| EncryptAction encryptAction = new EncryptAction(); | ||
|
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. Названия переменных |
||
| result = encryptAction.execute(text, key); | ||
|
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-case с ручным созданием объектов, используйте Map<Integer, CryptoAction> для реализации паттерна Strategy. Это избавит от 'лестницы' условий. |
||
| } | ||
| case 2 -> { | ||
| int key = readInt(scanner, message); | ||
| DecryptAction decryptAction = new DecryptAction(); | ||
| result = decryptAction.execute(text, key); | ||
| } | ||
| case 3 -> { | ||
| BruteForceAction bf = new BruteForceAction(); | ||
| BruteForceAction.Result best = bf.findBestByStopWords(text); | ||
|
|
||
| if (best != null) { | ||
| System.out.println("\n Brute-force result:"); | ||
| System.out.println("Key: " + best.key + " Score: " + best.score); | ||
| System.out.println("Candidate text:\n" + best.text); | ||
| result = best.text; | ||
| } else { | ||
| System.out.println("No likely candidate found by brute force."); | ||
| } | ||
| } | ||
| default -> System.out.println("Unknown action. Exiting."); | ||
| } | ||
| if (result != null) { | ||
| System.out.println("\n=== Result ==="); | ||
| System.out.println(result); | ||
|
|
||
| System.out.println("\nDo you want to save the result to a file? (y/n)"); | ||
| String answer = scanner.nextLine().trim().toLowerCase(); | ||
| if (answer.equals("y")) { | ||
| System.out.println("Enter path to file: "); | ||
| String filePath = scanner.nextLine().trim(); | ||
|
|
||
| FileWriterService fileWriter = new FileWriterService(); | ||
| fileWriter.writeFile(filePath, result); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static int readInt(Scanner scanner, String prompt) { | ||
| while (true) { | ||
| System.out.println(prompt); | ||
| String line = scanner.nextLine().trim(); | ||
| if (line.isEmpty()) { | ||
| System.out.println("Please enter 1, 2, or 3, then press Enter."); | ||
| continue; | ||
| } | ||
| if (!line.matches("\\d+")) { | ||
| System.out.println("Please enter a valid integer."); | ||
| continue; | ||
| } | ||
| try { | ||
| return Integer.parseInt(line); | ||
| } catch (NumberFormatException e) { | ||
| System.out.println("Number too large. Try again."); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package com.javarush.emirzakov.model; | ||
|
|
||
| public class Alphabet { | ||
| private 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. Поле |
||
|
|
||
| private static final char[] ENGLISH = "abcdefghijklmnopqrstuvwxyz".toCharArray(); | ||
|
|
||
| private static final char[] RUSSIAN = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя".toCharArray(); | ||
|
|
||
|
|
||
| public Alphabet(String text) { | ||
| if (text.matches(".*[а-яА-ЯёЁ].*")) { | ||
| ALPHABET = RUSSIAN; | ||
|
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. Логика выбора алфавита в конструкторе нарушает SRP. Лучше использовать паттерн Factory или статические фабричные методы, возвращающие разные экземпляры Alphabet. |
||
| } else { | ||
| ALPHABET = ENGLISH; | ||
| } | ||
| } | ||
|
|
||
| public int getCharIndex(char c) { | ||
| for (int i = 0; i < ALPHABET.length; i++) { | ||
| if (Character.toLowerCase(ALPHABET[i]) == Character.toLowerCase(c)) { | ||
|
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. Линейный поиск в массиве |
||
| return i; | ||
| } | ||
| } | ||
| return -1; | ||
| } | ||
|
|
||
| public char getCharByIndex(int index) { | ||
| int i = index % ALPHABET.length; | ||
| if (i < 0) i += ALPHABET.length; | ||
| return ALPHABET[i]; | ||
| } | ||
|
|
||
| public int length() { | ||
| return ALPHABET.length; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.javarush.emirzakov.model; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.HashSet; | ||
| import java.util.Set; | ||
|
|
||
| public final class StopWords { | ||
|
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. Класс StopWords стоит сделать Enum или использовать Sealed class, если планируется разная логика для разных языков. |
||
| public static final Set<String> RUSSIAN = new HashSet<>(Arrays.asList( | ||
| "и", "в", "на", "не", "что", "как", "я", "он", "она", "но", "с", | ||
|
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. Используйте Set.of(...) вместо HashSet(Arrays.asList(...)). В Java 9+ это создает немодифицируемое множество и выглядит чище. |
||
| "за", "по", "то", "этот", "это", "для", "вы", "мы", "они" | ||
| )); | ||
| public static final Set<String> ENGLISH = new HashSet<>(Arrays.asList( | ||
| "and", "the", "in", "of", "to", "a", "is", "it", "by", "for", | ||
| "on", "that", "this", "with", "as", "you", "I", "we", "he", "she" | ||
| )); | ||
|
|
||
| private StopWords() { | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| package com.javarush.emirzakov.service; | ||
|
|
||
| import com.javarush.emirzakov.model.Alphabet; | ||
| import com.javarush.emirzakov.model.StopWords; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| public class BruteForceAction { | ||
|
|
||
| public static class Result { | ||
| public final int key; | ||
| public final 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. Используйте Java 16+ Records вместо вложенного статического класса Result. Это сократит код и сделает данные иммутабельными (Data Transfer Object). |
||
| public final int score; | ||
|
|
||
| public Result(int key, String text, int score) { | ||
| this.key = key; | ||
| this.text = text; | ||
| this.score = score; | ||
| } | ||
| } | ||
|
|
||
| public Result findBestByStopWords(String cipherText) { | ||
| if (cipherText == null || cipherText.isBlank()) return null; | ||
|
|
||
| DecryptAction decryptAction = new DecryptAction(); | ||
| int N = new Alphabet(cipherText).length(); | ||
|
|
||
| int bestScore = -1; | ||
| Result best = null; | ||
|
|
||
|
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. Метод |
||
| for (int k = 0; k < N; k++) { | ||
| String candidate = decryptAction.execute(cipherText, k); | ||
| String sample = shortenForAnalysis(candidate); | ||
| String norm = TextUtils.normalize(sample); | ||
| List<String> tokens = TextUtils.tokenize(norm); | ||
|
|
||
| int rusMatches = countStopWords(tokens, StopWords.RUSSIAN); | ||
| int engMatches = countStopWords(tokens, StopWords.ENGLISH); | ||
| int matches = Math.max(rusMatches, engMatches); | ||
|
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 (matches > bestScore) { | ||
| bestScore = matches; | ||
|
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. Магическое число 1000 для |
||
| best = new Result(k, candidate, matches); | ||
| } | ||
| } | ||
| return best; | ||
| } | ||
|
|
||
| private String shortenForAnalysis(String text) { | ||
| int maxLen = 1000; | ||
| if (text == null) return ""; | ||
| if (text.length() <= maxLen) return text; | ||
|
|
||
| int cutIndex = text.lastIndexOf(' ', maxLen); | ||
| if (cutIndex == -1) { | ||
| cutIndex = maxLen; | ||
| } | ||
| return text.substring(0, cutIndex).trim(); | ||
| } | ||
|
|
||
| private int countStopWords(List<String> tokens, Set<String> stopSet) { | ||
| if (tokens == null || tokens.isEmpty() || stopSet == null || stopSet.isEmpty()) return 0; | ||
| int count = 0; | ||
| for (String t : tokens) { | ||
| if (stopSet.contains(t)) count++; | ||
| } | ||
| return count; | ||
| } | ||
|
|
||
|
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. Используйте Stream API для подсчета слов: |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.javarush.emirzakov.service; | ||
|
|
||
| import com.javarush.emirzakov.model.Alphabet; | ||
|
|
||
| public abstract class CryptoAction { | ||
|
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. Сделайте CryptoAction sealed интерфейсом, разрешая только конкретные реализации (Encrypt/Decrypt). Это обеспечит безопасность иерархии в Java 21. |
||
|
|
||
| protected char shiftChar(char c, int key, Alphabet alphabet) { | ||
| if (!Character.isLetter(c)) return c; | ||
|
|
||
| boolean isUpper = Character.isUpperCase(c); | ||
| char lower = Character.toLowerCase(c); | ||
| int index = alphabet.getCharIndex(lower); | ||
|
|
||
| if (index == -1) { | ||
| return c; | ||
| } | ||
| char shifted = alphabet.getCharByIndex(index + key); | ||
| return isUpper ? Character.toUpperCase(shifted) : shifted; | ||
| } | ||
|
|
||
|
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. В Java 21+ для выбора реализации или обработки данных стоит использовать Pattern Matching для switch, если планируется расширение типов действий. |
||
| public abstract String execute(String text, int key); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.javarush.emirzakov.service; | ||
|
|
||
| import com.javarush.emirzakov.model.Alphabet; | ||
|
|
||
| public class DecryptAction extends CryptoAction { | ||
|
|
||
| @Override | ||
| public String execute(String text, int key) { | ||
| Alphabet alphabet = new Alphabet(text); | ||
| StringBuilder result = new StringBuilder(); | ||
| for (int i = 0; i < text.length(); i++) { | ||
| char ch = text.charAt(i); | ||
| result.append(shiftChar(ch, -key, alphabet)); | ||
| } | ||
| return result.toString(); | ||
| } | ||
| } |
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.
Никогда не 'проглатывайте' исключения, возвращая пустую строку. Выбрасывайте кастомное RuntimeException или используйте запечатанные интерфейсы (Sealed Interfaces) для обработки ошибок в Java 21 стиле.