Skip to content

Commit 14bf5ea

Browse files
Merge pull request #790: added option to quote csv values containing whitespaces, fixed benchmark workflow file
2 parents ff745a3 + 6cc3bb1 commit 14bf5ea

File tree

17 files changed

+294
-264
lines changed

17 files changed

+294
-264
lines changed

.github/workflows/benchmark.yml

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: MiniExcel Benchmarks
22

33
on:
4+
workflow_dispatch:
45
release:
56
types: [published]
67

@@ -29,19 +30,14 @@ jobs:
2930
env:
3031
BenchmarkMode: Automatic
3132
BenchmarkSection: query
32-
- name: Commit report
33-
working-directory: ./benchmarks
34-
run: |
35-
cp -r ./MiniExcel.Benchmarks/BenchmarkDotNet.Artifacts/results ./
36-
git config user.name github-actions
37-
git config user.email [email protected]
38-
git pull
39-
cd ./results
40-
git add '*.md'
41-
git commit -am "Automated benchmark report - query section"
42-
git push --force
43-
env:
44-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
- name: Renaming result file
34+
run: mv MiniExcelLibs.Benchmarks.XlsxBenchmark-report-github.md query-benchmark.md
35+
working-directory: ./benchmarks/MiniExcel.Benchmarks/BenchMarkDotNet.Artifacts/results
36+
- name: Save benchmark results
37+
uses: actions/upload-artifact@v4
38+
with:
39+
name: query-benchmark-result
40+
path: ./benchmark/MiniExcel.Benchmarks/BenchMarkDotNet.Artifacts/results/*.md
4541

4642
CreateBenchmark:
4743
runs-on: ubuntu-latest
@@ -64,20 +60,15 @@ jobs:
6460
env:
6561
BenchmarkMode: Automatic
6662
BenchmarkSection: create
67-
- name: Commit report
68-
working-directory: ./benchmarks
69-
run: |
70-
cp -r ./MiniExcel.Benchmarks/BenchmarkDotNet.Artifacts/results ./
71-
git config user.name github-actions
72-
git config user.email [email protected]
73-
git pull
74-
cd ./results
75-
git add '*.md'
76-
git commit -am "Automated benchmark report - create section"
77-
git push --force
78-
env:
79-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80-
63+
- name: Renaming result file
64+
run: mv MiniExcelLibs.Benchmarks.XlsxBenchmark-report-github.md create-benchmark.md
65+
working-directory: ./benchmarks/MiniExcel.Benchmarks/BenchMarkDotNet.Artifacts/results
66+
- name: Save benchmark results
67+
uses: actions/upload-artifact@v4
68+
with:
69+
name: create-benchmark-result
70+
path: ./benchmark/MiniExcel.Benchmarks/BenchMarkDotNet.Artifacts/results/*.md
71+
8172
TemplateBenchmark:
8273
runs-on: ubuntu-latest
8374

@@ -99,16 +90,33 @@ jobs:
9990
env:
10091
BenchmarkMode: Automatic
10192
BenchmarkSection: template
102-
- name: Commit report
103-
working-directory: ./benchmarks
93+
- name: Renaming result file
94+
run: mv MiniExcelLibs.Benchmarks.XlsxBenchmark-report-github.md template-benchmark.md
95+
working-directory: ./benchmarks/MiniExcel.Benchmarks/BenchMarkDotNet.Artifacts/results
96+
- name: Save benchmark results
97+
uses: actions/upload-artifact@v4
98+
with:
99+
name: template-benchmark-result
100+
path: ./benchmark/MiniExcel.Benchmarks/BenchMarkDotNet.Artifacts/results/*.md
101+
102+
PushBenchmarksResults:
103+
runs-on: ubuntu-latest
104+
needs: [ QueryBenchmark, CreateBenchmark, TemplateBenchmark ]
105+
106+
steps:
107+
- uses: actions/checkout@v4
108+
with:
109+
fetch-depth: 0
110+
- name: Fetch benchmark results
111+
uses: actions/download-artifact@v4
112+
with:
113+
path: ./benchmarks/results
114+
merge-multiple: true
115+
- name: Commit reports
116+
working-directory: ./benchmarks/results
104117
run: |
105-
cp -r ./MiniExcel.Benchmarks/BenchmarkDotNet.Artifacts/results ./
106118
git config user.name github-actions
107119
git config user.email [email protected]
108-
git pull
109-
cd ./results
110-
git add '*.md'
111-
git commit -am "Automated benchmark report - template section"
112-
git push --force
113-
env:
114-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
120+
git add ./*.md
121+
git commit -am "Automated benchmark report - ${{ github.ref_name }}"
122+
git push origin master --force-with-lease

.github/workflows/dotnet.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ jobs:
1414
steps:
1515
- uses: actions/checkout@v4
1616
- name: Setup .NET 8
17-
uses: actions/setup-dotnet@v1
17+
uses: actions/setup-dotnet@v4
1818
with:
1919
dotnet-version: 8.0.x
2020
- name: Setup .NET 9
21-
uses: actions/setup-dotnet@v1
21+
uses: actions/setup-dotnet@v4
2222
with:
2323
dotnet-version: 9.0.x
2424
- name: Restore dependencies

MiniExcel.sln

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs and setting", "Docs an
1616
README.md = README.md
1717
README.zh-CN.md = README.zh-CN.md
1818
README.zh-Hant.md = README.zh-Hant.md
19+
.github\workflows\benchmark.yml = .github\workflows\benchmark.yml
20+
.github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
1921
EndProjectSection
2022
EndProject
2123
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CC1E0601-AEC9-42D7-8F6A-3FB3939EED16}"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1517,7 +1517,7 @@ MiniExcel.AddPicture(path, images);
15171517
#### 7. Get Sheets Dimension
15181518

15191519
```csharp
1520-
var dim = MiniExcel.GetSheetsDimensions(path);
1520+
var dim = MiniExcel.GetSheetDimensions(path);
15211521
```
15221522

15231523
### Examples:

benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
14-
<PackageReference Include="ClosedXML" Version="0.102.3" />
15-
<PackageReference Include="ClosedXML.Report" Version="0.2.11" />
16-
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
17-
<PackageReference Include="EPPlus" Version="7.7.0" />
13+
<PackageReference Include="BenchmarkDotNet" Version="0.15.0" />
14+
<PackageReference Include="ClosedXML" Version="0.105.0" />
15+
<PackageReference Include="ClosedXML.Report" Version="0.2.12" />
16+
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
17+
<PackageReference Include="EPPlus" Version="7.7.2" />
1818
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
19-
<PackageReference Include="System.IO.Packaging" Version="9.0.3" />
2019
</ItemGroup>
2120

2221
<ItemGroup>
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.ComponentModel;
2-
using BenchmarkDotNet.Running;
1+
using BenchmarkDotNet.Running;
32
using MiniExcelLibs.Benchmarks;
43
using MiniExcelLibs.Benchmarks.BenchmarkSections;
54

@@ -11,12 +10,19 @@
1110
"query" => typeof(QueryXlsxBenchmark),
1211
"create" => typeof(CreateXlsxBenchmark),
1312
"template" => typeof(TemplateXlsxBenchmark),
14-
_ => throw new InvalidEnumArgumentException($"Benchmark section {section} does not exist")
13+
_ => throw new ArgumentException($"Benchmark section {section} does not exist")
1514
};
1615

1716
BenchmarkRunner.Run(benchmark, new Config(), args);
1817
}
1918
else
19+
{
2020
BenchmarkSwitcher
21-
.FromTypes([typeof(CreateXlsxBenchmark)])
22-
.Run(args, new Config());
21+
.FromTypes(
22+
[
23+
typeof(QueryXlsxBenchmark),
24+
typeof(CreateXlsxBenchmark),
25+
typeof(TemplateXlsxBenchmark)
26+
])
27+
.Run(args, new Config());
28+
}

src/MiniExcel/Csv/CsvConfiguration.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ namespace MiniExcelLibs.Csv
66
{
77
public class CsvConfiguration : Configuration
88
{
9-
private static Encoding _defaultEncoding = new UTF8Encoding(true);
9+
private static readonly Encoding DefaultEncoding = new UTF8Encoding(true);
1010

1111
public char Seperator { get; set; } = ',';
1212
public string NewLine { get; set; } = "\r\n";
1313
public bool ReadLineBreaksWithinQuotes { get; set; } = true;
1414
public bool ReadEmptyStringAsNull { get; set; } = false;
1515
public bool AlwaysQuote { get; set; } = false;
16+
public bool QuoteWhitespaces { get; set; } = true;
1617
public Func<string, string[]> SplitFn { get; set; }
17-
public Func<Stream, StreamReader> StreamReaderFunc { get; set; } = (stream) => new StreamReader(stream, _defaultEncoding);
18-
public Func<Stream, StreamWriter> StreamWriterFunc { get; set; } = (stream) => new StreamWriter(stream, _defaultEncoding);
18+
public Func<Stream, StreamReader> StreamReaderFunc { get; set; } = (stream) => new StreamReader(stream, DefaultEncoding);
19+
public Func<Stream, StreamWriter> StreamWriterFunc { get; set; } = (stream) => new StreamWriter(stream, DefaultEncoding);
1920

20-
internal readonly static CsvConfiguration DefaultConfiguration = new CsvConfiguration();
21+
internal static readonly CsvConfiguration DefaultConfiguration = new CsvConfiguration();
2122
}
2223
}
2324

src/MiniExcel/Csv/CsvHelpers.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
internal static class CsvHelpers
44
{
55
/// <summary>If content contains special characters then use "{value}" format</summary>
6-
public static string ConvertToCsvValue(string value, bool alwaysQuote, char separator)
6+
public static string ConvertToCsvValue(string value, CsvConfiguration configuration)
77
{
88
if (value == null)
99
return string.Empty;
@@ -13,16 +13,14 @@ public static string ConvertToCsvValue(string value, bool alwaysQuote, char sepa
1313
value = value.Replace("\"", "\"\"");
1414
return $"\"{value}\"";
1515
}
16-
17-
if (value.Contains(separator.ToString()) || value.Contains(" ") || value.Contains("\n") || value.Contains("\r"))
18-
{
19-
return $"\"{value}\"";
20-
}
21-
22-
if (alwaysQuote)
23-
return $"\"{value}\"";
24-
25-
return value;
16+
17+
var shouldQuote = configuration.AlwaysQuote ||
18+
(configuration.QuoteWhitespaces && value.Contains(" ")) ||
19+
value.Contains(configuration.Seperator.ToString()) ||
20+
value.Contains("\r") ||
21+
value.Contains("\n");
22+
23+
return shouldQuote ? $"\"{value}\"" : value;
2624
}
2725
}
2826
}

src/MiniExcel/Csv/CsvWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public int Insert(bool overwriteSheet = false)
4949

5050
private void AppendColumn(StringBuilder rowBuilder, CellWriteInfo column)
5151
{
52-
rowBuilder.Append(CsvHelpers.ConvertToCsvValue(ToCsvString(column.Value, column.Prop), _configuration.AlwaysQuote, _configuration.Seperator));
52+
rowBuilder.Append(CsvHelpers.ConvertToCsvValue(ToCsvString(column.Value, column.Prop), _configuration));
5353
rowBuilder.Append(_configuration.Seperator);
5454
}
5555

@@ -63,7 +63,7 @@ private static void RemoveTrailingSeparator(StringBuilder rowBuilder)
6363

6464
private string GetHeader(List<ExcelColumnInfo> props) => string.Join(
6565
_configuration.Seperator.ToString(),
66-
props.Select(s => CsvHelpers.ConvertToCsvValue(s?.ExcelColumnName, _configuration.AlwaysQuote, _configuration.Seperator)));
66+
props.Select(s => CsvHelpers.ConvertToCsvValue(s?.ExcelColumnName, _configuration)));
6767

6868
private int WriteValues(object values)
6969
{

src/MiniExcel/OpenXml/SharedStringsDiskCache.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.IO;
5-
using System.Runtime.CompilerServices;
65
using System.Text;
76

87
namespace MiniExcelLibs.OpenXml
@@ -37,9 +36,11 @@ internal void Add(int index, string value)
3736
{
3837
if (index > _maxIndx)
3938
_maxIndx = index;
40-
byte[] valueBs = _encoding.GetBytes(value);
39+
40+
var valueBs = _encoding.GetBytes(value);
4141
if (value.Length > 32767) //check info length, becasue cell string max length is 47483647
42-
throw new ArgumentOutOfRangeException("Excel one cell max length is 32,767 characters");
42+
throw new ArgumentOutOfRangeException("", "Excel one cell max length is 32,767 characters");
43+
4344
_positionFs.Write(BitConverter.GetBytes(_valueFs.Position), 0, 4);
4445
_lengthFs.Write(BitConverter.GetBytes(valueBs.Length), 0, 4);
4546
_valueFs.Write(valueBs, 0, valueBs.Length);
@@ -49,16 +50,19 @@ private string GetValue(int index)
4950
{
5051
_positionFs.Position = index * 4;
5152
var bytes = new byte[4];
52-
_positionFs.Read(bytes, 0, 4);
53+
_ = _positionFs.Read(bytes, 0, 4);
5354
var position = BitConverter.ToInt32(bytes, 0);
55+
56+
bytes = new byte[4];
5457
_lengthFs.Position = index * 4;
55-
_lengthFs.Read(bytes, 0, 4);
58+
_ = _lengthFs.Read(bytes, 0, 4);
5659
var length = BitConverter.ToInt32(bytes, 0);
57-
_valueFs.Position = position;
60+
5861
bytes = new byte[length];
59-
_valueFs.Read(bytes, 0, length);
60-
var v = _encoding.GetString(bytes);
61-
return v;
62+
_valueFs.Position = position;
63+
_ = _valueFs.Read(bytes, 0, length);
64+
65+
return _encoding.GetString(bytes);
6266
}
6367

6468
protected virtual void Dispose(bool disposing)
@@ -69,15 +73,19 @@ protected virtual void Dispose(bool disposing)
6973
{
7074
// TODO: dispose managed state (managed objects)
7175
}
76+
7277
_positionFs.Dispose();
7378
if (File.Exists(_positionFs.Name))
7479
File.Delete(_positionFs.Name);
80+
7581
_lengthFs.Dispose();
7682
if (File.Exists(_lengthFs.Name))
7783
File.Delete(_lengthFs.Name);
84+
7885
_valueFs.Dispose();
7986
if (File.Exists(_valueFs.Name))
8087
File.Delete(_valueFs.Name);
88+
8189
_disposedValue = true;
8290
}
8391
}

0 commit comments

Comments
 (0)