Removed the Input property from the WordCoutner, added multiple methods
for processing data from different inputs, such as file, console or a string variable. Tidied up the command options parsing using Mono.Options package, added option -m for specifying amount of memory to use while reading data.
This commit is contained in:
parent
f7ab102def
commit
5c6b306b16
4 changed files with 111 additions and 126 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -7,4 +7,7 @@
|
|||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
11
Task3/Task3/Task3.csproj.bak
Normal file
11
Task3/Task3/Task3.csproj.bak
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
</Project>
|
|
@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Memory limit for reading data in bytes, set to 1_048_576 bytes
|
||||
/// </summary>
|
||||
public int MemoryLimit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// StreamReader for line by line data input, used by the TextProcessor to read data
|
||||
/// </summary>
|
||||
public StreamReader? Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// StreamWriter for line by line data output, used by the TextProcessor to write data
|
||||
/// </summary>
|
||||
|
@ -33,10 +28,10 @@ namespace Task3
|
|||
/// <summary>
|
||||
/// Creates an instance of WordCounter with the specified memory limit for reading data, input and output streams
|
||||
/// </summary>
|
||||
/// <param name="output">StreamWriter outputting the result</param>
|
||||
/// <param name="memoryLimit">Memory limit for reading data in bytes, set to 1_048_576 by default</param>
|
||||
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
|
|||
/// <summary>
|
||||
/// Creates an instance of WordCounter with the specified memory limit for reading data and an input stream. Sets console as an output
|
||||
/// </summary>
|
||||
/// <param name="input">StreamWriter supplying data to the WordCounter</param>
|
||||
/// <param name="memoryLimit">Memory limit for reading data in bytes, set to 1_048_576 by default</param>
|
||||
public WordCounter(StreamReader input, int memoryLimit = DEFAULT_MEMORY_LIMIT)
|
||||
{
|
||||
Input = input;
|
||||
Output = new StreamWriter(Console.OpenStandardOutput());
|
||||
MemoryLimit = memoryLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of WordCounter with the specified memory limit for reading data, an input and an output from specified files
|
||||
/// </summary>
|
||||
/// <param name="inputFile">Name of the file used for input</param>
|
||||
/// <param name="outputFile">Name of the file used for output</param>
|
||||
/// <param name="memoryLimit">Memory limit for reading data in bytes, set to 1_048_576 by default</param>
|
||||
public WordCounter(string inputFile, string outputFile, int memoryLimit = DEFAULT_MEMORY_LIMIT)
|
||||
{
|
||||
SetFileInput(inputFile);
|
||||
SetFileOutput(outputFile);
|
||||
MemoryLimit = memoryLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of WordCounter with the specified memory limit for reading data and an input from specified file. Sets console as an output
|
||||
/// </summary>
|
||||
/// <param name="inputFile">Name of the file used for input</param>
|
||||
/// <param name="memoryLimit">Memory limit for reading data in bytes, set to 1_048_576 by default</param>
|
||||
public WordCounter(string inputFile, int memoryLimit = DEFAULT_MEMORY_LIMIT)
|
||||
{
|
||||
SetFileInput(inputFile);
|
||||
Output = new StreamWriter(Console.OpenStandardOutput());
|
||||
MemoryLimit = memoryLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="memoryLimit">Memory limit for reading data in bytes, set to 1_048_576 by default</param>
|
||||
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
|
|||
/// <param name="doWrite">If true, writes the result to specified Output, true by default</param>
|
||||
/// <param name="doParallel">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</param>
|
||||
/// <returns>The result of data processing</returns>
|
||||
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<char[], char, int, long> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the WordCounter. Reads and processes data, then writes (if doWrite is true) and returns it
|
||||
/// </summary>
|
||||
/// <param name="data">A string to process</param>
|
||||
/// <param name="doWrite">If true, writes the result to specified Output, true by default</param>
|
||||
/// <param name="doParallel">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</param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the WordCounter using file as input. Reads and processes data, then writes (if doWrite is true) and returns it
|
||||
/// </summary>
|
||||
/// <param name="filePath">Name of the file used for input</param>
|
||||
/// <exception cref="FileNotFoundException">If the file doesn't exist</exception>
|
||||
/// <exception cref="ArgumentException">If the specified file is a directory</exception>
|
||||
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
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="filePath">Name of the file used for input</param>
|
||||
public void SetFileInput(string filePath)
|
||||
/// <param name="doWrite">If true, writes the result to specified Output, true by default</param>
|
||||
/// <param name="doParallel">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</param>
|
||||
/// <returns>The result of data processing</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -206,57 +200,6 @@ namespace Task3
|
|||
Output = writer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the WordCounter using console as input. Reads and processes data, then writes (if doWrite is true) and returns it
|
||||
/// </summary>
|
||||
/// <param name="doWrite">If true, writes the result to specified Output, true by default</param>
|
||||
/// <param name="doParallel">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</param>
|
||||
/// <returns>The result of data processing</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the WordCounter using string value as input. Reads and processes data, then writes (if doWrite is true) and returns it
|
||||
/// </summary>
|
||||
/// <param name="data">If true, writes the result to specified Output, true by default</param>
|
||||
/// <param name="doWrite">If true, writes the result to specified Output, false by default</param>
|
||||
/// <param name="doParallel">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</param>
|
||||
/// <returns>The result of data processing</returns>
|
||||
public long ProcessString(string data, bool doWrite = false, bool doParallel = false)
|
||||
{
|
||||
SetStringInput(data);
|
||||
return ProcessText(doWrite, doParallel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a string value as a WordCounter input
|
||||
/// </summary>
|
||||
/// <param name="data">A string value</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets console as WordCounter output
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue