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 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
|
try
|
||||||
{
|
{
|
||||||
WordCounter wordCounter = new WordCounter(path);
|
result = wordCounter.ProcessFileInput(path, doParallel: doParallel);
|
||||||
wordCounter.ProcessText(doParallel: doParallel);
|
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException ex)
|
catch (FileNotFoundException ex)
|
||||||
{
|
{
|
||||||
|
@ -18,9 +41,14 @@ if (args.Length > 0)
|
||||||
Console.WriteLine(ex.Message);
|
Console.WriteLine(ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else result = wordCounter.ProcessConsoleInput(doParallel: doParallel);
|
||||||
{
|
Console.WriteLine(result);
|
||||||
WordCounter wordCounter = new WordCounter();
|
|
||||||
wordCounter.ProcessTextConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</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
|
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>
|
/// <summary>
|
||||||
/// Memory limit for reading data in bytes, set to 1_048_576 bytes
|
/// Memory limit for reading data in bytes, set to 1_048_576 bytes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MemoryLimit { get; set; }
|
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>
|
/// <summary>
|
||||||
/// StreamWriter for line by line data output, used by the TextProcessor to write data
|
/// StreamWriter for line by line data output, used by the TextProcessor to write data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -33,10 +28,10 @@ namespace Task3
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an instance of WordCounter with the specified memory limit for reading data, input and output streams
|
/// Creates an instance of WordCounter with the specified memory limit for reading data, input and output streams
|
||||||
/// </summary>
|
/// </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>
|
/// <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;
|
Output = output;
|
||||||
MemoryLimit = memoryLimit;
|
MemoryLimit = memoryLimit;
|
||||||
}
|
}
|
||||||
|
@ -44,51 +39,8 @@ namespace Task3
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an instance of WordCounter with the specified memory limit for reading data and an input stream. Sets console as an output
|
/// Creates an instance of WordCounter with the specified memory limit for reading data and an input stream. Sets console as an output
|
||||||
/// </summary>
|
/// </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>
|
/// <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)
|
public WordCounter(int memoryLimit = DEFAULT_MEMORY_LIMIT) : this(new StreamWriter(Console.OpenStandardOutput()), memoryLimit) { }
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// simple write to output line by line
|
// simple write to output line by line
|
||||||
protected void WriteData(string result)
|
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="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>
|
/// <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>
|
/// <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)
|
Func<char[], char, int, long> countingFunc = doParallel ? CountWordsParallel : CountWordsNonParallel; // determine the processing function to use (no if statememnt on every read)
|
||||||
char prevChar = ' ';
|
char prevChar = ' ';
|
||||||
char[] currentBlock = new char[MemoryLimit]; // init buffer for the data
|
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;
|
long wordsAmount = 0;
|
||||||
// while there are chars being read, continue reading
|
// while there are chars being read, continue reading
|
||||||
while (charsRead > 0)
|
while (charsRead > 0)
|
||||||
{
|
{
|
||||||
wordsAmount += countingFunc(currentBlock, prevChar, charsRead); // process data
|
wordsAmount += countingFunc(currentBlock, prevChar, charsRead); // process data
|
||||||
prevChar = currentBlock[^1]; // set the prev char to the last char of the current block
|
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();
|
string result = wordsAmount.ToString();
|
||||||
if (doWrite) WriteData(result); // write data to output if needed
|
if (doWrite) WriteData(result); // write data to output if needed
|
||||||
// release resources
|
// release resources
|
||||||
Input.Dispose();
|
input.Dispose();
|
||||||
Output.Dispose();
|
|
||||||
return wordsAmount;
|
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)
|
private static FileInfo FindFile(string filePath)
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = new FileInfo(filePath);
|
FileInfo fileInfo = new FileInfo(filePath);
|
||||||
|
@ -183,15 +165,27 @@ namespace Task3
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="filePath">Name of the file used for input</param>
|
/// <param name="doWrite">If true, writes the result to specified Output, true by default</param>
|
||||||
public void SetFileInput(string filePath)
|
/// <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);
|
Console.WriteLine($"Подсчёт слов в тексте, нажмите Ctrl-Z для окончания ввода");
|
||||||
FileStream fileStream = fileInfo.OpenRead();
|
string? line;
|
||||||
StreamReader reader = new StreamReader(fileStream, TextEncoding);
|
MemoryStream stream = new MemoryStream();
|
||||||
Input = reader;
|
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>
|
/// <summary>
|
||||||
|
@ -206,57 +200,6 @@ namespace Task3
|
||||||
Output = writer;
|
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>
|
/// <summary>
|
||||||
/// Sets console as WordCounter output
|
/// Sets console as WordCounter output
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Reference in a new issue