diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0cd353c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.15) +project(CommandParser) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +file(GLOB SOURCES "src/*.cpp") + +# add_custom_target(run COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE} 'echo million | cat ../static/text.txt | cat ../static/zarplata.txt | wc -c | echo rambler | cat ../static/zarplata.txt | wc -c | cat ../static/text.txt | wc -c') +# MakeFile удобнее, так как там я мог make run сразу запускать файл с переданной строкой + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_link_libraries(${PROJECT_NAME}) + + +# Автоматический запуск тестов после сборки +add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure +) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eccd7bc --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +CXX := g++ +CXX_FLAGS := -Wall -Wextra -std=c++20 -ggdb + +BIN := bin +SRC := src +INCLUDE := include +LIB := lib + +LIBRARIES := +EXECUTABLE := main + +all: $(BIN)/$(EXECUTABLE) + +run: clean all + ./$(BIN)/$(EXECUTABLE) 'echo million | cat static/text.txt | cat static/zarplata.txt | wc -c | echo rambler | cat static/zarplata.txt | wc -c | cat static/text.txt | wc -c' + +$(BIN)/$(EXECUTABLE): $(wildcard $(SRC)/*.cpp) + $(CXX) $(CXX_FLAGS) -I$(INCLUDE) -L$(LIB) $^ -o $@ $(LIBRARIES) + +clean: + -rm -f $(BIN)/* \ No newline at end of file diff --git a/README.md b/README.md index c70cd1a..1c8667c 100644 --- a/README.md +++ b/README.md @@ -5,34 +5,40 @@ ## Platforms - Linux Ubuntu 22.04 --- -### DZ1 Вариант #13 -> Parse file - How many groups from place -- Необходимо разработать программу, которая обрабатывает данные онлайн сервиса MusicBrainz. -Датасет можно скачать на официальном сайте MusicBrainz https://data.metabrainz.org/pub/musicbrainz/data/fullexport/ -Необходимый файл mbdump.tar.bz2. -Описание датасета находится по ссылке https://musicbrainz.org/doc/MusicBrainz_Database/Schema -Например, для Артиста диаграмма находится по ссылке https://wiki.musicbrainz.org/images/7/7e/artist_entity_details.svg -Файл датасета представляет из себя сжатый архив из множества текстовых файлов, где данные располагаются в строчках, а поля разделены символом табуляции \t -Каждая таблица на схемах представлена отдельным файлом с аналогичным названием +### DZ1 Вариант #11 +- ​Необходимо написать программу, реализующую функционал потокового текстового редактора с использованием ООП. +Необходимо реализовать поддержку операций: +cat - передает следующей операции сначала строки поданные на вход, затем строки из файла filename +echo - игнорирует строки поданные на вход, передает следующей операции строку +Выход одной операции подается на вход другой операции, операции разделяются символом '|'. +Если следующей операции нет, то строки выводятся в стандартный вывод (на экран). -- Скачать датасет по ссылке можно с помощью команды wget, например -wget https://data.metabrainz.org/pub/musicbrainz/data/fullexport/20230304-002037/mbdump.tar.bz2 -Полученный файл нужно распаковать -tar xvf mbdump.tar.bz2 -Все файлы кроме gender, area, artist, artist_type можно удалить с помощью команды rm. -Программа должна принимать необходимые для работы имена распакованных файлов в качестве аргументов командной строки и выводить данные в стандартный вывод. -Программа не должна использовать интерактивный ввод с клавиатуры, например, "введите имя файла", "введите необходимый год", "ведите q чтобы выйти" - такими программами не удобно пользоваться и их разработка занимает больше времени. + +- Для реализации обработки строк утверждается следующий интерфейс IOperation. +Наследник IOperation обязан реализовать методы: +void ProcessLine(const std::string& str); +void HandleEndOfInput() = 0; +void SetNextOperation(<умный/сырой указатель/ссылка на IOperation>) = 0; + +- Каждая операция наследуется от интерфейса IOperation. +Программа должна парсить выражение, переданное в аргументах командной строки и строить конвеер из наследников IOperation. +Выражение можно парсить предельно строго ( например, недопускаются лишние пробелы или их отсутствие ) +Для упрощения парсера аргументы отдельных операций не допускают пробелы ( echo 1 2 3 недопустим как и cat file name.txt ) +Результат вычисления получается вызовом метода HandleEndOfInput у первой операции. +Пример (при вводе в терминале выражение с пробелами нужно обернуть, например, в одинарные кавычки) +./text_processor 'echo 1 | cat file_1.txt | cat file_2.txt' +1 +<содержимое file_1.txt> +<содержимое file_2.txt> - Ваш вариант: -Вывести число групп созданных в месте, название которого передано в аргументах командной строки +Добавьте к существующим операциям wc -c - операцию, которая выводит или передает дальше число суммарное число байт во всех переданных на вход строках #### Run application ``` -./bin/main +./bin/main ``` -#### For example (With output): +#### For example: ``` -./bin/main "Chile" -Input: Chile, Id = 43 -Groups from this place = 828 +./bin/main 'echo million$ | cat text.txt | cat zarplata.txt' ``` --- \ No newline at end of file diff --git a/include/CatOperation.hpp b/include/CatOperation.hpp new file mode 100644 index 0000000..afb8036 --- /dev/null +++ b/include/CatOperation.hpp @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include +#include "IOperation.hpp" + +class CatOperation final : public IOperation { +public: + CatOperation() : IOperation() {} + CatOperation(std::string& filename, std::shared_ptr&& operation=nullptr) : IOperation(std::move(operation)), filename_(filename) {} + + void ProcessLine(const std::string& pipeline) override; + void HandleEndOfInput() override; + +private: + std::string filename_; +}; \ No newline at end of file diff --git a/include/CommandParser.hpp b/include/CommandParser.hpp new file mode 100644 index 0000000..7d35347 --- /dev/null +++ b/include/CommandParser.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include +#include "IOperation.hpp" +#include "EchoOperation.hpp" +#include "CatOperation.hpp" +#include "WcOperation.hpp" + +class OperationsList { +public: + OperationsList() : pipeline_(), head_(), tail_() {} + // input копируем, так как в далньейшем в функции он будет меняться + OperationsList(const std::string& input); + + void AddOperation(std::shared_ptr&& operation); + void AddOperation(const std::string& token); + void RunOperations(); + +private: + const std::string pipeline_; + const std::string delimiter = " | "; + std::shared_ptr head_; + std::shared_ptr tail_; +}; \ No newline at end of file diff --git a/include/EchoOperation.hpp b/include/EchoOperation.hpp new file mode 100644 index 0000000..9ce6385 --- /dev/null +++ b/include/EchoOperation.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include "IOperation.hpp" + +class EchoOperation final : public IOperation { +public: + EchoOperation() : IOperation() {} + EchoOperation(std::string& str, std::shared_ptr&& operation=nullptr) : IOperation(std::move(operation)), str_(str) {} + + void ProcessLine(const std::string& pipeline) override; + void HandleEndOfInput() override; + +private: + std::string str_; +}; \ No newline at end of file diff --git a/include/IOperation.hpp b/include/IOperation.hpp new file mode 100644 index 0000000..cdad323 --- /dev/null +++ b/include/IOperation.hpp @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +class IOperation { +public: + IOperation() : next_operation_() {} + IOperation(std::shared_ptr&& operation) : next_operation_(std::move(operation)) {} + + virtual void ProcessLine(const std::string& pipeline) = 0; + virtual void HandleEndOfInput() = 0; + virtual void SetNextOperation(std::shared_ptr&& operation); + + virtual ~IOperation() {} + + std::shared_ptr next_operation_; +}; \ No newline at end of file diff --git a/include/WcOperation.hpp b/include/WcOperation.hpp new file mode 100644 index 0000000..01b6ee6 --- /dev/null +++ b/include/WcOperation.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include +#include +#include "IOperation.hpp" + +class WcOperation final : public IOperation { +public: + WcOperation() : IOperation() {} + WcOperation(std::string& str, std::shared_ptr&& operation=nullptr) : IOperation(std::move(operation)), str_(str) {} + + void ProcessLine(const std::string& pipeline) override; + void HandleEndOfInput() override; + +private: + std::string str_; +}; \ No newline at end of file diff --git a/src/CatOperation.cpp b/src/CatOperation.cpp new file mode 100644 index 0000000..27d9c6c --- /dev/null +++ b/src/CatOperation.cpp @@ -0,0 +1,28 @@ +#include "CatOperation.hpp" +#include + +void CatOperation::ProcessLine(const std::string& pipeline) { + std::ifstream file_(filename_); + if (!file_.is_open()) { + throw std::runtime_error("Cannot open file: " + filename_); + } + + // создаем std::stringstream и передаем в него буфер файла + std::stringstream file_stream; + file_stream << file_.rdbuf(); + + // получаем содержимое файла в виде строки + std::string file_contents = file_stream.str(); + + // передаем pipeline + file_contents в метод ProcessLine следующей операции + if (next_operation_) { + next_operation_->ProcessLine(pipeline + file_contents); + } else { + std::cout << pipeline << std::endl << file_contents << std::endl; + } +} + + +void CatOperation::HandleEndOfInput() { + ProcessLine(""); +} \ No newline at end of file diff --git a/src/CommandParser.cpp b/src/CommandParser.cpp new file mode 100644 index 0000000..53a614e --- /dev/null +++ b/src/CommandParser.cpp @@ -0,0 +1,66 @@ +#include "CommandParser.hpp" + +OperationsList::OperationsList(const std::string& input) : pipeline_(input), head_(nullptr), tail_(nullptr) { + size_t pos = 0; + std::string token; + std::string_view cursor(pipeline_); + + // Пока есть разделители, запоминаем позицию + while ((pos = cursor.find(delimiter)) != std::string::npos) { + // Получаем подстроку до разделителя + token = cursor.substr(0, pos); + // Удаляем ее из pipeline_ учитывая длину разделителя + cursor = cursor.substr(pos + delimiter.length()); + AddOperation(token); + } + + // Последняя команда указана без разделителя, обрабатываем ее + if (!cursor.empty()) { + AddOperation(std::string(cursor)); + } +} + +void OperationsList::AddOperation(const std::string& token){ + const std::string ECHO = "echo"; + const std::string CAT = "cat"; + const std::string WC = "wc -c"; + + if (token.starts_with(ECHO)) { + std::string str = token.substr(ECHO.length() + 1); // Достаем переданную строку + AddOperation(std::make_shared(str)); + return; + } + + if (token.starts_with(CAT)) { + std::string file = token.substr(CAT.length() + 1); // Достаем переданный файл + AddOperation(std::make_shared(file)); + return; + } + + if (token.starts_with(WC)) { + std::string str; + if (token.length() > WC.length()) { + str = token.substr(WC.length() + 1); // Достаем переданную строку + } + AddOperation(std::make_shared(str)); + return; + } +} + + +void OperationsList::AddOperation(std::shared_ptr&& operation) { + if (!head_) { + head_ = std::move(operation); + tail_ = head_; + } else { + tail_->SetNextOperation(std::move(operation)); + tail_ = tail_->next_operation_; + } +} + + +void OperationsList::RunOperations() { + if (head_) { + head_->HandleEndOfInput(); + } +} \ No newline at end of file diff --git a/src/EchoOperation.cpp b/src/EchoOperation.cpp new file mode 100644 index 0000000..07fdb66 --- /dev/null +++ b/src/EchoOperation.cpp @@ -0,0 +1,14 @@ +#include "EchoOperation.hpp" + +void EchoOperation::ProcessLine(const std::string& pipeline) { + if(next_operation_) { + next_operation_->ProcessLine(str_); + } else { + std::cout << str_ << std::endl; + } +} + + +void EchoOperation::HandleEndOfInput() { + ProcessLine(""); +} \ No newline at end of file diff --git a/src/IOperation.cpp b/src/IOperation.cpp new file mode 100644 index 0000000..319a16a --- /dev/null +++ b/src/IOperation.cpp @@ -0,0 +1,6 @@ +#include "IOperation.hpp" + +// Реализуем по умолчанию метод интерфейсного класса +void IOperation::SetNextOperation(std::shared_ptr&& operation) { + next_operation_ = std::move(operation); +} \ No newline at end of file diff --git a/src/WcOperation.cpp b/src/WcOperation.cpp new file mode 100644 index 0000000..4615ec9 --- /dev/null +++ b/src/WcOperation.cpp @@ -0,0 +1,18 @@ +#include "WcOperation.hpp" + +void WcOperation::ProcessLine(const std::string& pipeline) { + // вычисляем суммарное число байт в строке full_string = pipeline + str_ + int total_bytes = sizeof(char) * (pipeline.size() + str_.size()); + + // передаем pipeline + str_ в метод ProcessLine следующей операции + if (next_operation_) { + next_operation_->ProcessLine(std::to_string(total_bytes)); + } else { + std::cout << total_bytes << std::endl; + } +} + + +void WcOperation::HandleEndOfInput() { + ProcessLine(""); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..46e967d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,20 @@ +#include +#include "CommandParser.hpp" + + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Run program with arguments like: 'echo | cat | echo million | cat Elon.txt'\n"; + return 1; + } + try{ + OperationsList list(argv[1]); + list.RunOperations(); + } + catch(const std::exception& e){ + std::cerr << e.what() << '\n'; + } + + + return 0; +} \ No newline at end of file diff --git a/static/text.txt b/static/text.txt new file mode 100644 index 0000000..f9d88db --- /dev/null +++ b/static/text.txt @@ -0,0 +1,26 @@ +​Необходимо написать программу, реализующую функционал потокового текстового редактора с использованием ООП. +Необходимо реализовать поддержку операций: +cat - передает следующей операции сначала строки поданные на вход, затем строки из файла filename +echo - игнорирует строки поданные на вход, передает следующей операции строку +Выход одной операции подается на вход другой операции, операции разделяются символом '|'. +Если следующей операции нет, то строки выводятся в стандартный вывод (на экран). + +Для реализации обработки строк утверждается следующий интерфейс IOperation. +Наследник IOperation обязан реализовать методы: +void ProcessLine(const std::string& str); +void HandleEndOfInput() = 0; +void SetNextOperation(<умный/сырой указатель/ссылка на IOperation>) = 0; + +Каждая операция наследуется от интерфейса IOperation. +Программа должна парсить выражение, переданное в аргументах командной строки и строить конвеер из наследников IOperation. +Выражение можно парсить предельно строго ( например, недопускаются лишние пробелы или их отсутствие ) +Для упрощения парсера аргументы отдельных операций не допускают пробелы ( echo 1 2 3 недопустим как и cat file name.txt ) +Результат вычисления получается вызовом метода HandleEndOfInput у первой операции. +Пример (при вводе в терминале выражение с пробелами нужно обернуть, например, в одинарные кавычки) +./text_processor 'echo 1 | cat file_1.txt | cat file_2.txt' +1 +<содержимое file_1.txt> +<содержимое file_2.txt> + +Ваш вариант: +Добавьте к существующим операциям wc -c - операцию, которая выводит или передает дальше число суммарное число байт во всех переданных на вход строках \ No newline at end of file diff --git a/static/zarplata.txt b/static/zarplata.txt new file mode 100644 index 0000000..3141f69 --- /dev/null +++ b/static/zarplata.txt @@ -0,0 +1,9 @@ +На ваш счет зачислено 2_000_000$ +К переводу прикреплено сообщение: +"Дорогая, спасибой за вечер! Особенно его окончание!" +Баланса: все еще не хватает для беззаботной жизни + + + + +