Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
next-version: 1.1.0
next-version: 1.1.5
tag-prefix: '[vV]'
mode: ContinuousDeployment
branches:
Expand Down
36 changes: 19 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# <img src="https://github.com/CodeShayk/parsley.net/blob/master/Images/ninja-icon-16.png" alt="ninja" style="width:30px;"/> Parsley.Net v1.1.0
# <img src="https://github.com/CodeShayk/parsley.net/blob/master/Images/ninja-icon-16.png" alt="ninja" style="width:30px;"/> Parsley.Net v1.1.5
[![NuGet version](https://badge.fury.io/nu/Parsley.Net.svg)](https://badge.fury.io/nu/Parsley.Net) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/CodeShayk/Parsley.Net/blob/master/LICENSE.md)
[![GitHub Release](https://img.shields.io/github/v/release/CodeShayk/Parsley.Net?logo=github&sort=semver)](https://github.com/CodeShayk/Parsley.Net/releases/latest)
[![master-build](https://github.com/CodeShayk/parsley.net/actions/workflows/Master-Build.yml/badge.svg)](https://github.com/CodeShayk/parsley.net/actions/workflows/Master-Build.yml)
Expand Down Expand Up @@ -44,9 +44,9 @@ NuGet\Install-Package Parsley.Net

Please see below.
```
public interface IParser
{
/// <summary>
public interface IParser
{
/// <summary>
/// Parses a file at the specified filepath into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
Expand All @@ -63,20 +63,20 @@ Please see below.
T[] Parse<T>(string[] lines) where T : IFileLine, new();

/// <summary>
/// Parses a stream of delimiter separated records into an array of objects of type T.
/// Parses an array of bytes of delimiter separated records into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream"></param>
/// <param name="bytes"></param>
/// <returns></returns>
T[] Parse<T>(Stream stream) where T : IFileLine, new();
T[] Parse<T>(byte[] bytes, Encoding encoding = null) where T : IFileLine, new();

/// <summary>
/// Parses an array of bytes of delimiter separated records into an array of objects of type T.
/// Parses a stream of delimiter separated records into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="bytes"></param>
/// <param name="stream"></param>
/// <returns></returns>
T[] Parse<T>(byte[] bytes) where T : IFileLine, new();
T[] Parse<T>(Stream stream, Encoding encoding = null) where T : IFileLine, new();

/// <summary>
/// Asynchronously parses a file at the specified filepath into an array of objects of type T.
Expand All @@ -95,20 +95,21 @@ Please see below.
Task<T[]> ParseAsync<T>(string[] lines) where T : IFileLine, new();

/// <summary>
/// Asynchronously parses a stream of delimiter separated strings into an array of objects of type T.
/// Asynchronously parses an array of bytes of delimiter separated records into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream"></param>
/// <param name="bytes"></param>
/// <returns></returns>
Task<T[]> ParseAsync<T>(Stream stream) where T : IFileLine, new();
Task<T[]> ParseAsync<T>(byte[] bytes, Encoding encoding = null) where T : IFileLine, new();

/// <summary>
/// Asynchronously parses an array of bytes of delimiter separated records into an array of objects of type T.
/// Asynchronously parses a stream of delimiter separated strings into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="bytes"></param>
/// <param name="stream"></param>
/// <returns></returns>
Task<T[]> ParseAsync<T>(byte[] bytes) where T : IFileLine, new();
}
Task<T[]> ParseAsync<T>(Stream stream, Encoding encoding = null) where T : IFileLine, new();
}
```
To initialise `Parser` class you could do it manually or use dependency injection as shown below. The parser class has parameterised constructor that takes the delimiter character to initialise the instance. Default character is ',' (comma) to initialise the parser for a CSV file parsing.

Expand Down Expand Up @@ -338,6 +339,7 @@ The main branch is now on .NET 9.0.
| -------- | --------|
| [`v1.0.0`](https://github.com/CodeShayk/parsley.net/tree/v1.0.0) | [Notes](https://github.com/CodeShayk/Parsley.Net/releases/tag/v1.0.0) |
| [`v1.1.0`](https://github.com/CodeShayk/parsley.net/tree/v1.1.0) | [Notes](https://github.com/CodeShayk/Parsley.Net/releases/tag/v1.1.0) |
| [`v1.1.5`](https://github.com/CodeShayk/parsley.net/tree/v1.1.5) | [Notes](https://github.com/CodeShayk/Parsley.Net/releases/tag/v1.1.5) |

## Credits
Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)
35 changes: 18 additions & 17 deletions src/Parsley/IParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace parsley
Expand All @@ -19,23 +20,23 @@ public interface IParser
/// <typeparam name="T"></typeparam>
/// <param name="lines"></param>
/// <returns></returns>
T[] Parse<T>(string[] lines) where T : IFileLine, new();
T[] Parse<T>(string[] lines) where T : IFileLine, new();

/// <summary>
/// Parses a stream of delimiter separated records into an array of objects of type T.
/// Parses an array of bytes of delimiter separated records into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream"></param>
/// <param name="bytes"></param>
/// <returns></returns>
T[] Parse<T>(Stream stream) where T : IFileLine, new();

T[] Parse<T>(byte[] bytes, Encoding encoding = null) where T : IFileLine, new();
/// <summary>
/// Parses an array of bytes of delimiter separated records into an array of objects of type T.
/// Parses a stream of delimiter separated records into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="bytes"></param>
/// <param name="stream"></param>
/// <returns></returns>
T[] Parse<T>(byte[] bytes) where T : IFileLine, new();
T[] Parse<T>(Stream stream, Encoding encoding = null) where T : IFileLine, new();

/// <summary>
/// Asynchronously parses a file at the specified filepath into an array of objects of type T.
Expand All @@ -52,21 +53,21 @@ public interface IParser
/// <param name="lines"></param>
/// <returns></returns>
Task<T[]> ParseAsync<T>(string[] lines) where T : IFileLine, new();

/// <summary>
/// Asynchronously parses a stream of delimiter separated strings into an array of objects of type T.
/// Asynchronously parses an array of bytes of delimiter separated records into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream"></param>
/// <param name="bytes"></param>
/// <returns></returns>
Task<T[]> ParseAsync<T>(Stream stream) where T : IFileLine, new();

Task<T[]> ParseAsync<T>(byte[] bytes, Encoding encoding = null) where T : IFileLine, new();
/// <summary>
/// Asynchronously parses an array of bytes of delimiter separated records into an array of objects of type T.
/// Asynchronously parses a stream of delimiter separated strings into an array of objects of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="bytes"></param>
/// <param name="stream"></param>
/// <returns></returns>
Task<T[]> ParseAsync<T>(byte[] bytes) where T : IFileLine, new();
Task<T[]> ParseAsync<T>(Stream stream, Encoding encoding = null) where T : IFileLine, new();
}
}
79 changes: 35 additions & 44 deletions src/Parsley/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,8 @@ public Parser(char delimiter)
}

private string[] ReadToLines(string path)
{
var lines = new List<string>();

foreach (var line in File.ReadLines(path))
{
if (line != null)
lines.Add(line);
}

return lines.ToArray<string>();
{
return File.ReadAllLines(path);
}

private T ParseLine<T>(string line) where T : IFileLine, new()
Expand Down Expand Up @@ -165,13 +157,13 @@ private string[] GetDelimiterSeparatedValues(string line)
return values;
}

public T[] Parse<T>(Stream stream) where T : IFileLine, new()
public T[] Parse<T>(Stream stream, Encoding encoding = null) where T : IFileLine, new()
{
if (stream == null || stream.Length == 0)
return Array.Empty<T>();

var lines = new List<string>();
using (var reader = new StreamReader(stream, Encoding.UTF8))
using (var reader = new StreamReader(stream, encoding ?? Encoding.UTF8))
{
string line;
while ((line = reader.ReadLine()) != null)
Expand All @@ -186,53 +178,36 @@ private string[] GetDelimiterSeparatedValues(string line)
return lines.Any() ? Parse<T>(lines.ToArray()) : Array.Empty<T>();
}

public T[] Parse<T>(byte[] bytes) where T : IFileLine, new()
public T[] Parse<T>(byte[] bytes, Encoding encoding = null) where T : IFileLine, new()
{
if (bytes == null || bytes.Length == 0)
return Array.Empty<T>();

return Parse<T>(new MemoryStream(bytes));
return Parse<T>(new MemoryStream(bytes), encoding);
}

public async Task<T[]> ParseAsync<T>(string filepath) where T : IFileLine, new()
{
if (string.IsNullOrEmpty(filepath) || !File.Exists(filepath))
return Array.Empty<T>();

var lines = await Task.Run(() => ReadToLines(filepath));
var lines = await Task.FromResult(ReadToLines(filepath));

return await ParseAsync<T>(lines);
}
public async Task<T[]> ParseAsync<T>(string[] lines) where T : IFileLine, new()
}

public async Task<T[]> ParseAsync<T>(byte[] bytes, Encoding encoding = null) where T : IFileLine, new()
{
if (lines == null || lines.Length == 0)
if (bytes == null || bytes.Length == 0)
return Array.Empty<T>();

var list = new T[lines.Length];
var index = 0;
var inputs = lines.Select(line => new { Line = line, Index = index++ });

foreach (var input in inputs)
{
if (string.IsNullOrWhiteSpace(input.Line))
continue;

var parsedLine = await Task.Run(() => ParseLine<T>(input.Line));
return await ParseAsync<T>(new MemoryStream(bytes), encoding);
}

if (parsedLine != null)
{
parsedLine.Index = input.Index;
list[parsedLine.Index] = parsedLine;
}
}
return list;
}

public async Task<T[]> ParseAsync<T>(Stream stream) where T : IFileLine, new()
public async Task<T[]> ParseAsync<T>(Stream stream, Encoding encoding = null) where T : IFileLine, new()
{
var lines = new List<string>();
using (var reader = new StreamReader(stream, Encoding.UTF8))
using (var reader = new StreamReader(stream, encoding ?? Encoding.UTF8))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
Expand All @@ -244,14 +219,30 @@ private string[] GetDelimiterSeparatedValues(string line)
}

return lines.Any() ? await ParseAsync<T>(lines.ToArray()) : Array.Empty<T>();
}
}

public async Task<T[]> ParseAsync<T>(byte[] bytes) where T : IFileLine, new()
public async Task<T[]> ParseAsync<T>(string[] lines) where T : IFileLine, new()
{
if (bytes == null || bytes.Length == 0)
if (lines == null || lines.Length == 0)
return Array.Empty<T>();

return await ParseAsync<T>(new MemoryStream(bytes));
var index = 0;
var indexedLines = lines
.Select((line) => new { Line = line, Index = index++ })
.Where(x => !string.IsNullOrWhiteSpace(x.Line))
.ToArray();

var tasks = indexedLines
.Select(x => Task.Run(() => new { x.Index, Parsed = ParseLine<T>(x.Line) }))
.ToArray();

var results = await Task.WhenAll(tasks);

var list = new T[tasks.Length];
foreach (var result in results)
list[result.Index] = result.Parsed;

return list;
}
}
}
7 changes: 2 additions & 5 deletions src/Parsley/Parsley.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageProjectUrl>https://github.com/CodeShayk/Parsley.Net/wiki</PackageProjectUrl>
<RepositoryUrl>https://github.com/CodeShayk/Parsley.Net</RepositoryUrl>
<PackageReleaseNotes>
v1.1.0 - Targets .Net9.0, .NetStandard2.1, .NetStandard2.0, and .NetFramework4.6.4. <br/>
* Includes core functionality for parsing delimiter separated files.
* Provided Sync and Async parsing methods</PackageReleaseNotes>
<Version>1.1.0</Version>
<PackageReleaseNotes>v1.1.5 - performance improvements in async parsing.</PackageReleaseNotes>
<Version>1.1.5</Version>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<AssemblyName>Parsley.Net</AssemblyName>
</PropertyGroup>
Expand Down
7 changes: 4 additions & 3 deletions tests/Parsley.Tests/ParserFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void TestParseForDependencyInjectionShouldReturnInitialisedInstance()
}

[Test]
public void TestParseWithFileInputShouldReturnCorrectlyParsedArray()
public void TestParseWithFilePathShouldReturnCorrectlyParsedArray()
{
var filePath = Path.Combine(Environment.CurrentDirectory, "TestFile.txt");

Expand Down Expand Up @@ -221,14 +221,15 @@ public async Task TestParseAsyncWithStringArrayInputShouldReturnCorrectlyParsedA
var lines = new[]
{
"GB-01|Bob Marley|True|Free",
"UH-02|John Walsh McKinsey|False|Paid"
"UH-02|John Walsh McKinsey|False|Paid",
"UH-03|Fred Wigg|False|Paid",
};

parser = new Parser('|');

var parsed = await parser.ParseAsync<FileLine>(lines);

Assert.That(parsed.Length, Is.EqualTo(2));
Assert.That(parsed.Length, Is.EqualTo(3));

Assert.That(parsed[0].Code.Batch, Is.EqualTo("GB"));
Assert.That(parsed[0].Code.SerialNo, Is.EqualTo(1));
Expand Down
Loading