Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty items #168

Merged
merged 3 commits into from
Dec 2, 2023
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
22 changes: 14 additions & 8 deletions src/FileParser/Implementations/ParsedFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
/// </summary>
/// <param name="path">FilePath</param>
/// <param name="existingSeparator">Word separator (space by default)</param>
public ParsedFile(string path, char[] existingSeparator)
: this(path, new string(existingSeparator))
/// <param name="ignoreEmptyItems"></param>
public ParsedFile(string path, char[] existingSeparator, bool ignoreEmptyItems = true)
: this(path, new string(existingSeparator), ignoreEmptyItems)
{
}

Expand All @@ -41,9 +42,10 @@
/// </summary>
/// <param name="path">FilePath</param>
/// <param name="existingSeparator">Word separator (space by default)</param>
public ParsedFile(string path, string? existingSeparator = null)
/// <param name="ignoreEmptyItems"></param>
public ParsedFile(string path, string? existingSeparator = null, bool ignoreEmptyItems = true)
#pragma warning disable CS0618 // Type or member is obsolete - will keep it as private
: base(ParseFile(path, existingSeparator))
: base(ParseFile(path, existingSeparator, ignoreEmptyItems))
#pragma warning restore CS0618 // Type or member is obsolete
{
}
Expand Down Expand Up @@ -207,14 +209,15 @@
/// </summary>
/// <param name="path"></param>
/// <param name="existingSeparator">Word separator</param>
/// <param name="ignoreEmptyItems"></param>
/// <exception cref="FileNotFoundException"></exception>
/// <exception cref="DirectoryNotFoundException"></exception>
/// <exception cref="IOException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <returns></returns>
[Obsolete("This method exposes internal functionality and was made public accidentally. It will be removed in next major release, please used ParsedFile constructor instead.")]

Check warning on line 219 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)

Check warning on line 219 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)
public static Queue<IParsedLine> ParseFile(string path, string? existingSeparator = null)
public static Queue<IParsedLine> ParseFile(string path, string? existingSeparator = null, bool ignoreEmptyItems = true)
{
Queue<IParsedLine> parsedFile = new();

Expand All @@ -226,14 +229,14 @@
{
string? original_line = reader.ReadLine();

// TODO: Evaluate if is it worth giving the user the option of detecting these kind of lines?

Check warning on line 232 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 232 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 232 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
if (string.IsNullOrWhiteSpace(original_line))
{
continue;
}
// end TODO

Check warning on line 237 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 237 in src/FileParser/Implementations/ParsedFile.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

IParsedLine parsedLine = new ParsedLine(ProcessLine(original_line, existingSeparator));
IParsedLine parsedLine = new ParsedLine(ProcessLine(original_line, existingSeparator, ignoreEmptyItems));
parsedFile.Enqueue(parsedLine);
}
}
Expand All @@ -248,13 +251,16 @@
}
}

private static ICollection<string> ProcessLine(string original_line, string? separator)
private static ICollection<string> ProcessLine(string original_line, string? separator, bool ignoreEmptyItems)
{
List<string> wordsInLine = original_line
.Split(separator?.ToCharArray())
.Select(str => str.Trim()).ToList();

wordsInLine.RemoveAll(string.IsNullOrWhiteSpace); // Probably not needed, but just in case
if (ignoreEmptyItems)
{
wordsInLine.RemoveAll(string.IsNullOrWhiteSpace);
}

return wordsInLine;
}
Expand Down
5 changes: 5 additions & 0 deletions src/FileParser/Utils/StringConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
/// <returns></returns>
public static T Convert<T>(string str, TypeConverter? typeConverter = null)
{
if (str.Length == 0)
{
return default!;
}

return default(T) switch
{
short => (T)(object)short.Parse(str),
Expand All @@ -22,7 +27,7 @@
uint => (T)(object)uint.Parse(str),
long => (T)(object)long.Parse(str),
ulong => (T)(object)ulong.Parse(str),
// double => (T)(object)double.Parse(str), // Causes issues with commas and dots separators, since it doesn't use the logic in TConverter.ParseDouble();

Check warning on line 30 in src/FileParser/Utils/StringConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 30 in src/FileParser/Utils/StringConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)
_ => GenericConvert<T>(str, typeConverter)
};
}
Expand Down
113 changes: 113 additions & 0 deletions tests/FileParser.Test/ParsedFileTest/ExtractTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,118 @@ public void ExtractChar()

Assert.Equal(line1 + '\\' + line2 + '\\', parsedFile.ToString());
}

[Fact]
public void MultipleCharWordSeparator()
{
const string fileName = "MultipleCharWordSeparator.txt";
const string line1 = " FirstName|| LastName ||Age||EyeColor ";
const string line2 = " John|| Doe||66 || Brown";

var expectedFirstLine = new[] { "FirstName", "LastName", "Age", "EyeColor" };
var expectedSecondLine = new[] { "John", "Doe", "66", "Brown" };

using (StreamWriter writer = new(fileName))
{
writer.WriteLine(line1);
writer.WriteLine(line2);
}

IParsedFile file = new ParsedFile(fileName, "||");

int index = 0;
var line = file.NextLine();
while (!line.Empty)
{
var item = line.NextElement<string>();
Assert.Equal(expectedFirstLine[index++], item);
}

line = file.NextLine();
Assert.Equal(expectedSecondLine, line.ToList<string>());

Assert.True(file.Empty);
}

[Fact]
public void ParseCsvFileIntoStrings()
{
const string fileName = "ParseCsvFileIntoStrings.txt";
const string line1 = "FirstName,LastName,Age,EyeColor";
const string line2 = "John,Doe,66,Brown";
const string line3 = "Cthulhu,,1000,Black";
const string line4 = "Bugs,Bunny,33,White";

using (StreamWriter writer = new(fileName))
{
writer.WriteLine(line1);
writer.WriteLine(line2);
writer.WriteLine(line3);
writer.WriteLine(line4);
}

IParsedFile file = new ParsedFile(fileName, ",", ignoreEmptyItems: false);

var headerLine = file.NextLine();
var buckets = new List<List<string>>(headerLine.ToList<string>().Select(header => new List<string>(1000) { ModifyString(header) }));

while (!file.Empty)
{
var line = file.NextLine().ToList<string>();
for (int index = 0; index < line.Count; ++index)
{
buckets[index].Add(ModifyString(line[index]));
}
}

static string ModifyString(string str) => str.ToLowerInvariant();

Assert.Equal(new[] { "firstname", "john", "cthulhu", "bugs" }, buckets[0]);
Assert.Equal(new[] { "lastname", "doe", "", "bunny" }, buckets[1]);
Assert.Equal(new[] { "age", "66", "1000", "33" }, buckets[2]);
Assert.Equal(new[] { "eyecolor", "brown", "black", "white" }, buckets[3]);
}

[Fact]
public void ParseCsvFile()
{
const string fileName = "ParseCsvFile.txt";
const string line1 = "Name,Age,Score";
const string line2 = "Abraham,10,9.05";
const string line3 = "Babilon,,64.05";
const string line4 = "Croc,99,";

using (StreamWriter writer = new(fileName))
{
writer.WriteLine(line1);
writer.WriteLine(line2);
writer.WriteLine(line3);
writer.WriteLine(line4);
}

IParsedFile file = new ParsedFile(fileName, ",", ignoreEmptyItems: false);

file.NextLine();

var line = file.NextLine();

Assert.Equal("Abraham", line.NextElement<string>());
Assert.Equal(10, line.NextElement<int>());
Assert.Equal(9.05, line.NextElement<double>());

line = file.NextLine();

Assert.Equal("Babilon", line.NextElement<string>());
Assert.Equal(default, line.NextElement<int>());
Assert.Equal(64.05, line.NextElement<double>());

line = file.NextLine();

Assert.Equal("Croc", line.NextElement<string>());
Assert.Equal(99, line.NextElement<int>());
Assert.Equal(default, line.NextElement<double>());

Assert.True(file.Empty);
}
}
}
Loading