diff --git a/README.md b/README.md
index bd04703..b47dd0a 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,6 @@
```
file-filter-utility/
├── pom.xml
-├── README.md
└── src/
└── main/
└── java/
diff --git a/file-filter/.gitignore b/file-filter/.gitignore
new file mode 100644
index 0000000..480bdf5
--- /dev/null
+++ b/file-filter/.gitignore
@@ -0,0 +1,39 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+.kotlin
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/file-filter/.idea/.gitignore b/file-filter/.idea/.gitignore
new file mode 100644
index 0000000..ab1f416
--- /dev/null
+++ b/file-filter/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/file-filter/.idea/encodings.xml b/file-filter/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/file-filter/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/file-filter/.idea/misc.xml b/file-filter/.idea/misc.xml
new file mode 100644
index 0000000..d2b5d0f
--- /dev/null
+++ b/file-filter/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/file-filter/.idea/vcs.xml b/file-filter/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/file-filter/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/file-filter/dependency-reduced-pom.xml b/file-filter/dependency-reduced-pom.xml
new file mode 100644
index 0000000..16dbc8d
--- /dev/null
+++ b/file-filter/dependency-reduced-pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+ com.filefilter
+ file-filter-utility
+ File Filter Utility
+ 1.0.0
+ Утилита для фильтрации содержимого файлов по типам данных
+
+
+
+ maven-compiler-plugin
+ 3.11.0
+
+ 11
+ 11
+ UTF-8
+
+
+
+ maven-shade-plugin
+ 3.5.1
+
+
+ package
+
+ shade
+
+
+
+
+ com.filefilter.FileFilterApp
+
+
+ file-filter
+
+
+
+
+
+
+
+ UTF-8
+ 11
+ 4.7.5
+ 11
+
+
diff --git a/file-filter/pom.xml b/file-filter/pom.xml
new file mode 100644
index 0000000..9174e6d
--- /dev/null
+++ b/file-filter/pom.xml
@@ -0,0 +1,70 @@
+
+
+ 4.0.0
+
+ com.filefilter
+ file-filter-utility
+ 1.0.0
+ jar
+
+ File Filter Utility
+ Утилита для фильтрации содержимого файлов по типам данных
+
+
+ UTF-8
+ 11
+ 11
+ 4.7.5
+
+
+
+
+
+ info.picocli
+ picocli
+ ${picocli.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.1
+
+
+ package
+
+ shade
+
+
+
+
+ com.filefilter.FileFilterApp
+
+
+ file-filter
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/file-filter/src/main/java/com/filefilter/FileFilterApp.java b/file-filter/src/main/java/com/filefilter/FileFilterApp.java
new file mode 100644
index 0000000..ff6d11f
--- /dev/null
+++ b/file-filter/src/main/java/com/filefilter/FileFilterApp.java
@@ -0,0 +1,240 @@
+package com.filefilter;
+
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
+
+@Command(name = "file-filter",
+ mixinStandardHelpOptions = true,
+ version = "1.0",
+ description = "Утилита фильтрации содержимого файлов по типам данных")
+public class FileFilterApp implements Runnable {
+
+ @Parameters(description = "Входные файлы для обработки")
+ private List inputFiles = new ArrayList<>();
+
+ @Option(names = {"-o", "--output"},
+ description = "Путь для выходных файлов (по умолчанию: текущая директория)")
+ private String outputPath = ".";
+
+ @Option(names = {"-p", "--prefix"},
+ description = "Префикс имен выходных файлов")
+ private String prefix = "";
+
+ @Option(names = {"-a", "--append"},
+ description = "Режим добавления в существующие файлы")
+ private boolean appendMode = false;
+
+ @Option(names = {"-s", "--short-stats"},
+ description = "Краткая статистика")
+ private boolean shortStats = false;
+
+ @Option(names = {"-f", "--full-stats"},
+ description = "Полная статистика")
+ private boolean fullStats = false;
+
+ private Statistics statistics;
+ private FileWriterManager writerManager;
+
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new FileFilterApp()).execute(args);
+ System.exit(exitCode);
+ }
+
+ @Override
+ public void run() {
+ if (inputFiles == null || inputFiles.isEmpty()) {
+ System.err.println("Ошибка: не указаны входные файлы");
+ return;
+ }
+
+ statistics = new Statistics();
+ writerManager = new FileWriterManager(outputPath, prefix, appendMode);
+
+ // Обработка файлов
+ for (String inputFile : inputFiles) {
+ processFile(inputFile);
+ }
+
+ // Закрытие всех выходных файлов
+ writerManager.closeAll();
+
+ // Вывод статистики
+ printStatistics();
+ }
+
+ private void processFile(String filename) {
+ Path filePath = Paths.get(filename);
+
+ if (!Files.exists(filePath)) {
+ System.err.println("Предупреждение: файл не найден: " + filename);
+ return;
+ }
+
+ if (!Files.isReadable(filePath)) {
+ System.err.println("Предупреждение: файл недоступен для чтения: " + filename);
+ return;
+ }
+
+ try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
+ String line;
+ int lineNumber = 0;
+
+ while ((line = reader.readLine()) != null) {
+ lineNumber++;
+ processLine(line, filename, lineNumber);
+ }
+ } catch (IOException e) {
+ System.err.println("Ошибка при чтении файла " + filename + ": " + e.getMessage());
+ }
+ }
+
+ private void processLine(String line, String filename, int lineNumber) {
+ if (line.trim().isEmpty()) {
+ return; // Пропускаем пустые строки
+ }
+
+ DataType type = detectType(line);
+
+ try {
+ switch (type) {
+ case INTEGER:
+ long intValue = Long.parseLong(line.trim());
+ writerManager.writeInteger(intValue);
+ statistics.addInteger(intValue);
+ break;
+
+ case FLOAT:
+ double floatValue = Double.parseDouble(line.trim());
+ writerManager.writeFloat(floatValue);
+ statistics.addFloat(floatValue);
+ break;
+
+ case STRING:
+ writerManager.writeString(line);
+ statistics.addString(line);
+ break;
+ }
+ } catch (IOException e) {
+ System.err.println("Ошибка при записи данных из файла " + filename +
+ ", строка " + lineNumber + ": " + e.getMessage());
+ } catch (NumberFormatException e) {
+ // Если парсинг не удался, считаем строкой
+ try {
+ writerManager.writeString(line);
+ statistics.addString(line);
+ } catch (IOException ioException) {
+ System.err.println("Ошибка при записи строки из файла " + filename +
+ ", строка " + lineNumber + ": " + ioException.getMessage());
+ }
+ }
+ }
+
+ private DataType detectType(String line) {
+ String trimmed = line.trim();
+
+ // Проверяем на целое число
+ try {
+ Long.parseLong(trimmed);
+ return DataType.INTEGER;
+ } catch (NumberFormatException e) {
+ // Не целое число
+ }
+
+ // Проверяем на вещественное число
+ try {
+ Double.parseDouble(trimmed);
+ return DataType.FLOAT;
+ } catch (NumberFormatException e) {
+ // Не число
+ }
+
+ // Иначе это строка
+ return DataType.STRING;
+ }
+
+ private void printStatistics() {
+ System.out.println();
+
+ if (!shortStats && !fullStats) {
+ return; // Статистика не запрошена
+ }
+
+ if (shortStats) {
+ printShortStatistics();
+ } else if (fullStats) {
+ printFullStatistics();
+ }
+ }
+
+ private void printShortStatistics() {
+ System.out.println("=== Краткая статистика ===");
+
+ if (statistics.getIntegerCount() > 0) {
+ System.out.println("Целые числа: " + statistics.getIntegerCount() + " элементов");
+ }
+
+ if (statistics.getFloatCount() > 0) {
+ System.out.println("Вещественные числа: " + statistics.getFloatCount() + " элементов");
+ }
+
+ if (statistics.getStringCount() > 0) {
+ System.out.println("Строки: " + statistics.getStringCount() + " элементов");
+ }
+
+ if (statistics.getIntegerCount() == 0 &&
+ statistics.getFloatCount() == 0 &&
+ statistics.getStringCount() == 0) {
+ System.out.println("Данные не обработаны");
+ }
+ }
+
+ private void printFullStatistics() {
+ System.out.println("=== Полная статистика ===");
+
+ if (statistics.getIntegerCount() > 0) {
+ System.out.println("\nЦелые числа:");
+ System.out.println(" Количество: " + statistics.getIntegerCount());
+ System.out.println(" Минимум: " + statistics.getIntegerMin());
+ System.out.println(" Максимум: " + statistics.getIntegerMax());
+ System.out.println(" Сумма: " + statistics.getIntegerSum());
+ System.out.println(" Среднее: " + statistics.getIntegerAverage());
+ }
+
+ if (statistics.getFloatCount() > 0) {
+ System.out.println("\nВещественные числа:");
+ System.out.println(" Количество: " + statistics.getFloatCount());
+ System.out.println(" Минимум: " + statistics.getFloatMin());
+ System.out.println(" Максимум: " + statistics.getFloatMax());
+ System.out.println(" Сумма: " + statistics.getFloatSum());
+ System.out.println(" Среднее: " + statistics.getFloatAverage());
+ }
+
+ if (statistics.getStringCount() > 0) {
+ System.out.println("\nСтроки:");
+ System.out.println(" Количество: " + statistics.getStringCount());
+ System.out.println(" Длина самой короткой: " + statistics.getStringMinLength());
+ System.out.println(" Длина самой длинной: " + statistics.getStringMaxLength());
+ }
+
+ if (statistics.getIntegerCount() == 0 &&
+ statistics.getFloatCount() == 0 &&
+ statistics.getStringCount() == 0) {
+ System.out.println("Данные не обработаны");
+ }
+ }
+
+ private enum DataType {
+ INTEGER, FLOAT, STRING
+ }
+}
\ No newline at end of file
diff --git a/file-filter/src/main/java/com/filefilter/FileWriterManager.java b/file-filter/src/main/java/com/filefilter/FileWriterManager.java
new file mode 100644
index 0000000..fd76a1f
--- /dev/null
+++ b/file-filter/src/main/java/com/filefilter/FileWriterManager.java
@@ -0,0 +1,135 @@
+package com.filefilter;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+
+/**
+ * Менеджер для управления записью данных в выходные файлы
+ */
+public class FileWriterManager {
+
+ private static final String INTEGERS_FILE = "integers.txt";
+ private static final String FLOATS_FILE = "floats.txt";
+ private static final String STRINGS_FILE = "strings.txt";
+
+ private final String outputPath;
+ private final String prefix;
+ private final boolean appendMode;
+
+ private BufferedWriter integerWriter;
+ private BufferedWriter floatWriter;
+ private BufferedWriter stringWriter;
+
+ /**
+ * Конструктор менеджера файлов
+ *
+ * @param outputPath путь для выходных файлов
+ * @param prefix префикс имен файлов
+ * @param appendMode режим добавления в файлы
+ */
+ public FileWriterManager(String outputPath, String prefix, boolean appendMode) {
+ this.outputPath = outputPath != null ? outputPath : ".";
+ this.prefix = prefix != null ? prefix : "";
+ this.appendMode = appendMode;
+ }
+
+ /**
+ * Записать целое число в файл
+ */
+ public void writeInteger(long value) throws IOException {
+ if (integerWriter == null) {
+ integerWriter = createWriter(INTEGERS_FILE);
+ }
+ integerWriter.write(String.valueOf(value));
+ integerWriter.newLine();
+ }
+
+ /**
+ * Записать вещественное число в файл
+ */
+ public void writeFloat(double value) throws IOException {
+ if (floatWriter == null) {
+ floatWriter = createWriter(FLOATS_FILE);
+ }
+ floatWriter.write(String.valueOf(value));
+ floatWriter.newLine();
+ }
+
+ /**
+ * Записать строку в файл
+ */
+ public void writeString(String value) throws IOException {
+ if (stringWriter == null) {
+ stringWriter = createWriter(STRINGS_FILE);
+ }
+ stringWriter.write(value);
+ stringWriter.newLine();
+ }
+
+ /**
+ * Создать writer для указанного файла
+ */
+ private BufferedWriter createWriter(String filename) throws IOException {
+ Path dirPath = Paths.get(outputPath);
+
+ // Создаем директорию если не существует
+ if (!Files.exists(dirPath)) {
+ try {
+ Files.createDirectories(dirPath);
+ } catch (IOException e) {
+ throw new IOException("Не удалось создать директорию: " + outputPath +
+ ". Причина: " + e.getMessage(), e);
+ }
+ }
+
+ Path filePath = dirPath.resolve(prefix + filename);
+
+ StandardOpenOption[] options;
+ if (appendMode) {
+ options = new StandardOpenOption[]{
+ StandardOpenOption.CREATE,
+ StandardOpenOption.APPEND
+ };
+ } else {
+ options = new StandardOpenOption[]{
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING
+ };
+ }
+
+ try {
+ return Files.newBufferedWriter(filePath, StandardCharsets.UTF_8, options);
+ } catch (IOException e) {
+ throw new IOException("Не удалось создать файл: " + filePath +
+ ". Причина: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Закрыть все открытые файлы
+ */
+ public void closeAll() {
+ closeWriter(integerWriter, "integers");
+ closeWriter(floatWriter, "floats");
+ closeWriter(stringWriter, "strings");
+ }
+
+ /**
+ * Безопасно закрыть writer
+ */
+ private void closeWriter(BufferedWriter writer, String type) {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ System.err.println("Предупреждение: ошибка при закрытии файла " +
+ type + ": " + e.getMessage());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/file-filter/src/main/java/com/filefilter/Statistics.java b/file-filter/src/main/java/com/filefilter/Statistics.java
new file mode 100644
index 0000000..ba95920
--- /dev/null
+++ b/file-filter/src/main/java/com/filefilter/Statistics.java
@@ -0,0 +1,112 @@
+package com.filefilter;
+
+/**
+ * Класс для сбора статистики по обработанным данным
+ */
+public class Statistics {
+
+ // Статистика для целых чисел
+ private long integerCount = 0;
+ private long integerMin = Long.MAX_VALUE;
+ private long integerMax = Long.MIN_VALUE;
+ private long integerSum = 0;
+
+ // Статистика для вещественных чисел
+ private long floatCount = 0;
+ private double floatMin = Double.MAX_VALUE;
+ private double floatMax = Double.MIN_VALUE;
+ private double floatSum = 0.0;
+
+ // Статистика для строк
+ private long stringCount = 0;
+ private int stringMinLength = Integer.MAX_VALUE;
+ private int stringMaxLength = Integer.MIN_VALUE;
+
+ /**
+ * Добавить целое число в статистику
+ */
+ public void addInteger(long value) {
+ integerCount++;
+ integerMin = Math.min(integerMin, value);
+ integerMax = Math.max(integerMax, value);
+ integerSum += value;
+ }
+
+ /**
+ * Добавить вещественное число в статистику
+ */
+ public void addFloat(double value) {
+ floatCount++;
+ floatMin = Math.min(floatMin, value);
+ floatMax = Math.max(floatMax, value);
+ floatSum += value;
+ }
+
+ /**
+ * Добавить строку в статистику
+ */
+ public void addString(String value) {
+ stringCount++;
+ int length = value.length();
+ stringMinLength = Math.min(stringMinLength, length);
+ stringMaxLength = Math.max(stringMaxLength, length);
+ }
+
+ // Геттеры для целых чисел
+
+ public long getIntegerCount() {
+ return integerCount;
+ }
+
+ public long getIntegerMin() {
+ return integerCount > 0 ? integerMin : 0;
+ }
+
+ public long getIntegerMax() {
+ return integerCount > 0 ? integerMax : 0;
+ }
+
+ public long getIntegerSum() {
+ return integerSum;
+ }
+
+ public double getIntegerAverage() {
+ return integerCount > 0 ? (double) integerSum / integerCount : 0.0;
+ }
+
+ // Геттеры для вещественных чисел
+
+ public long getFloatCount() {
+ return floatCount;
+ }
+
+ public double getFloatMin() {
+ return floatCount > 0 ? floatMin : 0.0;
+ }
+
+ public double getFloatMax() {
+ return floatCount > 0 ? floatMax : 0.0;
+ }
+
+ public double getFloatSum() {
+ return floatSum;
+ }
+
+ public double getFloatAverage() {
+ return floatCount > 0 ? floatSum / floatCount : 0.0;
+ }
+
+ // Геттеры для строк
+
+ public long getStringCount() {
+ return stringCount;
+ }
+
+ public int getStringMinLength() {
+ return stringCount > 0 ? stringMinLength : 0;
+ }
+
+ public int getStringMaxLength() {
+ return stringCount > 0 ? stringMaxLength : 0;
+ }
+}
\ No newline at end of file