diff --git a/Task3/Task3/Program.cs b/Task3/Task3/Program.cs index e839438..d421133 100644 --- a/Task3/Task3/Program.cs +++ b/Task3/Task3/Program.cs @@ -1,13 +1,36 @@ using Task3; +using Mono.Options; -if (args.Length > 0) +string? path = null; +int memoryLimit = WordCounter.DEFAULT_MEMORY_LIMIT; +bool doParallel = false, showHelp = false; +var options = new OptionSet() +{ + { "f|file=", "the {FILE} to use as input", v => path = v }, + { "m|memory=", "the max amount of {MEMORY} used while reading data", v => + { + bool success = int.TryParse(v, out int memory); + if (success) memoryLimit = memory; + } + }, + { "p", "count words using multiple threads", v => doParallel = v != null }, + { "h", "show help", v => showHelp = v != null } +}; +options.Parse(args); + +if (showHelp) +{ + ShowHelp(options); + return; +} + +WordCounter wordCounter = new WordCounter(memoryLimit); +long result = 0; +if (path != null) { - string path = args[0]; - bool doParallel = args.Length > 1 && args[1] == "-p"; try { - WordCounter wordCounter = new WordCounter(path); - wordCounter.ProcessText(doParallel: doParallel); + result = wordCounter.ProcessFileInput(path, doParallel: doParallel); } catch (FileNotFoundException ex) { @@ -18,9 +41,14 @@ if (args.Length > 0) Console.WriteLine(ex.Message); } } -else -{ - WordCounter wordCounter = new WordCounter(); - wordCounter.ProcessTextConsole(); -} +else result = wordCounter.ProcessConsoleInput(doParallel: doParallel); +Console.WriteLine(result); +static void ShowHelp(OptionSet options) +{ + Console.WriteLine("Usage: ./Task3.exe [OPTIONS]+ message"); + Console.WriteLine("Counts amount of words in the specified input."); + Console.WriteLine(); + Console.WriteLine("Options:"); + options.WriteOptionDescriptions(Console.Out); +} diff --git a/Task3/Task3/Task3.csproj b/Task3/Task3/Task3.csproj index 9ad2a07..8a31a9e 100644 --- a/Task3/Task3/Task3.csproj +++ b/Task3/Task3/Task3.csproj @@ -7,4 +7,7 @@ enable + + + diff --git a/Task3/Task3/Task3.csproj.bak b/Task3/Task3/Task3.csproj.bak new file mode 100644 index 0000000..6e29ffb --- /dev/null +++ b/Task3/Task3/Task3.csproj.bak @@ -0,0 +1,11 @@ + + + + Exe + net6.0 + enable + enable + + + + diff --git a/Task3/Task3/WordCounter.cs b/Task3/Task3/WordCounter.cs index 48c34d6..13e131d 100644 --- a/Task3/Task3/WordCounter.cs +++ b/Task3/Task3/WordCounter.cs @@ -8,18 +8,13 @@ namespace Task3 { public class WordCounter { - protected const int DEFAULT_MEMORY_LIMIT = 1_048_576; // 1 Mb + public const int DEFAULT_MEMORY_LIMIT = 1_048_576; // 1 Mb /// /// Memory limit for reading data in bytes, set to 1_048_576 bytes /// public int MemoryLimit { get; set; } - /// - /// StreamReader for line by line data input, used by the TextProcessor to read data - /// - public StreamReader? Input { get; set; } - /// /// StreamWriter for line by line data output, used by the TextProcessor to write data /// @@ -33,10 +28,10 @@ namespace Task3 /// /// Creates an instance of WordCounter with the specified memory limit for reading data, input and output streams /// + /// StreamWriter outputting the result /// Memory limit for reading data in bytes, set to 1_048_576 by default - public WordCounter(StreamReader input, StreamWriter output, int memoryLimit = DEFAULT_MEMORY_LIMIT) + public WordCounter(StreamWriter output, int memoryLimit = DEFAULT_MEMORY_LIMIT) { - Input = input; Output = output; MemoryLimit = memoryLimit; } @@ -44,51 +39,8 @@ namespace Task3 /// /// Creates an instance of WordCounter with the specified memory limit for reading data and an input stream. Sets console as an output /// - /// StreamWriter supplying data to the WordCounter /// Memory limit for reading data in bytes, set to 1_048_576 by default - public WordCounter(StreamReader input, int memoryLimit = DEFAULT_MEMORY_LIMIT) - { - Input = input; - Output = new StreamWriter(Console.OpenStandardOutput()); - MemoryLimit = memoryLimit; - } - - /// - /// Creates an instance of WordCounter with the specified memory limit for reading data, an input and an output from specified files - /// - /// Name of the file used for input - /// Name of the file used for output - /// Memory limit for reading data in bytes, set to 1_048_576 by default - public WordCounter(string inputFile, string outputFile, int memoryLimit = DEFAULT_MEMORY_LIMIT) - { - SetFileInput(inputFile); - SetFileOutput(outputFile); - MemoryLimit = memoryLimit; - } - - /// - /// Creates an instance of WordCounter with the specified memory limit for reading data and an input from specified file. Sets console as an output - /// - /// Name of the file used for input - /// Memory limit for reading data in bytes, set to 1_048_576 by default - public WordCounter(string inputFile, int memoryLimit = DEFAULT_MEMORY_LIMIT) - { - SetFileInput(inputFile); - Output = new StreamWriter(Console.OpenStandardOutput()); - MemoryLimit = memoryLimit; - } - - /// - /// Creates an instance of WordCounter with the specified memory limit for reading data. - /// Input is set to null, should be specified somewhere later in code before calling the processing methods. - /// Sets console as an output - /// - /// Memory limit for reading data in bytes, set to 1_048_576 by default - public WordCounter(int memoryLimit = DEFAULT_MEMORY_LIMIT) - { - Output = new StreamWriter(Console.OpenStandardOutput()); - MemoryLimit = memoryLimit; - } + public WordCounter(int memoryLimit = DEFAULT_MEMORY_LIMIT) : this(new StreamWriter(Console.OpenStandardOutput()), memoryLimit) { } // simple write to output line by line protected void WriteData(string result) @@ -151,29 +103,59 @@ namespace Task3 /// If true, writes the result to specified Output, true by default /// If true, processes data in parallel by partitioning the blocks of data. Recommended to use with a memory limit of at least 100 Kb, true by default /// The result of data processing - public long ProcessText(bool doWrite = true, bool doParallel = true) + public long ProcessText(StreamReader input, bool doWrite = true, bool doParallel = true) { - if (Input == null) throw new ArgumentNullException("No input was found"); Func countingFunc = doParallel ? CountWordsParallel : CountWordsNonParallel; // determine the processing function to use (no if statememnt on every read) char prevChar = ' '; char[] currentBlock = new char[MemoryLimit]; // init buffer for the data - int charsRead = Input.ReadBlock(currentBlock, 0, MemoryLimit); // first read + int charsRead = input.ReadBlock(currentBlock, 0, MemoryLimit); // first read long wordsAmount = 0; // while there are chars being read, continue reading while (charsRead > 0) { wordsAmount += countingFunc(currentBlock, prevChar, charsRead); // process data prevChar = currentBlock[^1]; // set the prev char to the last char of the current block - charsRead = Input.ReadBlock(currentBlock, 0, MemoryLimit); // read data + charsRead = input.ReadBlock(currentBlock, 0, MemoryLimit); // read data } string result = wordsAmount.ToString(); if (doWrite) WriteData(result); // write data to output if needed // release resources - Input.Dispose(); - Output.Dispose(); + input.Dispose(); return wordsAmount; } + /// + /// Starts the WordCounter. Reads and processes data, then writes (if doWrite is true) and returns it + /// + /// A string to process + /// If true, writes the result to specified Output, true by default + /// If true, processes data in parallel by partitioning the blocks of data. Recommended to use with a memory limit of at least 100 Kb, true by default + /// + public long ProcessString(string data, bool doWrite = false, bool doParallel = false) + { + long wordsAmount; + if (doParallel) wordsAmount = CountWordsParallel(data.ToCharArray(), ' ', data.Length); + else wordsAmount = CountWordsNonParallel(data.ToCharArray(), ' ', data.Length); + string result = wordsAmount.ToString(); + if (doWrite) WriteData(result); // write data to output if needed + // release resources + return wordsAmount; + } + + /// + /// Starts the WordCounter using file as input. Reads and processes data, then writes (if doWrite is true) and returns it + /// + /// Name of the file used for input + /// If the file doesn't exist + /// If the specified file is a directory + public long ProcessFileInput(string filePath, bool doWrite = true, bool doParallel = true) + { + FileInfo fileInfo = FindFile(filePath); + FileStream fileStream = fileInfo.OpenRead(); + StreamReader fileReader = new StreamReader(fileStream, TextEncoding); + return ProcessText(fileReader, doWrite, doParallel); + } + private static FileInfo FindFile(string filePath) { FileInfo fileInfo = new FileInfo(filePath); @@ -183,15 +165,27 @@ namespace Task3 } /// - /// Sets a file as a WordCounter input + /// Starts the WordCounter using console as input. Reads and processes data, then writes (if doWrite is true) and returns it /// - /// Name of the file used for input - public void SetFileInput(string filePath) + /// If true, writes the result to specified Output, true by default + /// If true, processes data in parallel by partitioning the blocks of data. Recommended to use with a memory limit of at least 100 Kb, false by default + /// The result of data processing + public long ProcessConsoleInput(bool doWrite = true, bool doParallel = false) { - FileInfo fileInfo = FindFile(filePath); - FileStream fileStream = fileInfo.OpenRead(); - StreamReader reader = new StreamReader(fileStream, TextEncoding); - Input = reader; + Console.WriteLine($"Подсчёт слов в тексте, нажмите Ctrl-Z для окончания ввода"); + string? line; + MemoryStream stream = new MemoryStream(); + StreamWriter sw = new StreamWriter(stream); + while (true) + { + line = Console.ReadLine(); + if (line == null || line.Contains('\u001A')) break; + sw.WriteLine(line); + } + sw.Flush(); + stream.Position = 0; + StreamReader console = new StreamReader(stream); + return ProcessText(console, doWrite, doParallel); } /// @@ -206,57 +200,6 @@ namespace Task3 Output = writer; } - /// - /// Starts the WordCounter using console as input. Reads and processes data, then writes (if doWrite is true) and returns it - /// - /// If true, writes the result to specified Output, true by default - /// If true, processes data in parallel by partitioning the blocks of data. Recommended to use with a memory limit of at least 100 Kb, false by default - /// The result of data processing - public long ProcessTextConsole(bool doWrite = true, bool doParallel = false) - { - Console.WriteLine($"Подсчёт слов в тексте, нажмите Ctrl-Z для окончания ввода"); - string? line; - MemoryStream stream = new MemoryStream(); - StreamWriter sw = new StreamWriter(stream); - while (true) - { - line = Console.ReadLine(); - if (line == null || line.Contains('\u001A')) break; - sw.WriteLine(line); - } - sw.Flush(); - stream.Position = 0; - Input = new StreamReader(stream); - return ProcessText(doWrite, doParallel); - } - - /// - /// Starts the WordCounter using string value as input. Reads and processes data, then writes (if doWrite is true) and returns it - /// - /// If true, writes the result to specified Output, true by default - /// If true, writes the result to specified Output, false by default - /// If true, processes data in parallel by partitioning the blocks of data. Recommended to use with a memory limit of at least 100 Kb, false by default - /// The result of data processing - public long ProcessString(string data, bool doWrite = false, bool doParallel = false) - { - SetStringInput(data); - return ProcessText(doWrite, doParallel); - } - - /// - /// Sets a string value as a WordCounter input - /// - /// A string value - public void SetStringInput(string data) - { - MemoryStream stream = new MemoryStream(); - StreamWriter sw = new StreamWriter(stream); - sw.Write(data); - sw.Flush(); - stream.Position = 0; - Input = new StreamReader(stream); - } - /// /// Sets console as WordCounter output ///