77using System . Text . RegularExpressions ;
88using System . Threading ;
99using System . Threading . Tasks ;
10+ using MiniExcelLibs . Exceptions ;
1011
1112namespace MiniExcelLibs . Csv
1213{
1314 internal class CsvReader : IExcelReader
1415 {
1516 private Stream _stream ;
1617 private CsvConfiguration _config ;
18+
1719 public CsvReader ( Stream stream , IConfiguration configuration )
1820 {
19- this . _stream = stream ;
20- this . _config = configuration == null ? CsvConfiguration . DefaultConfiguration : ( CsvConfiguration ) configuration ;
21+ _stream = stream ;
22+ _config = configuration == null ? CsvConfiguration . DefaultConfiguration : ( CsvConfiguration ) configuration ;
2123 }
2224 public IEnumerable < IDictionary < string , object > > Query ( bool useHeaderRow , string sheetName , string startCell )
2325 {
2426 if ( startCell != "A1" )
25- throw new NotImplementedException ( "CSV not Implement startCell" ) ;
27+ throw new NotImplementedException ( "CSV does not implement parameter startCell" ) ;
28+
2629 if ( _stream . CanSeek )
2730 _stream . Position = 0 ;
31+
2832 var reader = _config . StreamReaderFunc ( _stream ) ;
33+ var firstRow = true ;
34+ var headRows = new Dictionary < int , string > ( ) ;
35+
36+ string row ;
37+ for ( var rowIndex = 1 ; ( row = reader . ReadLine ( ) ) != null ; rowIndex ++ )
2938 {
30- string [ ] read ;
31- var firstRow = true ;
32- Dictionary < int , string > headRows = new Dictionary < int , string > ( ) ;
33- string row ;
34- for ( var rowIndex = 1 ; ( row = reader . ReadLine ( ) ) != null ; rowIndex ++ )
39+ string finalRow = row ;
40+ if ( _config . ReadLineBreaksWithinQuotes )
3541 {
36-
37- string finalRow = row ;
38- if ( _config . ReadLineBreaksWithinQuotes )
42+ while ( finalRow . Count ( c => c == '"' ) % 2 != 0 )
3943 {
40- while ( finalRow . Count ( c => c == '"' ) % 2 != 0 )
44+ var nextPart = reader . ReadLine ( ) ;
45+ if ( nextPart == null )
4146 {
42- var nextPart = reader . ReadLine ( ) ;
43- if ( nextPart == null )
44- {
45- break ;
46- }
47- finalRow = string . Concat ( finalRow , _config . NewLine , nextPart ) ;
47+ break ;
4848 }
49+ finalRow = string . Concat ( finalRow , _config . NewLine , nextPart ) ;
4950 }
50- read = Split ( finalRow ) ;
51+ }
52+ var read = Split ( finalRow ) ;
5153
52- // invalid row check
53- if ( read . Length < headRows . Count )
54- {
55- var colIndex = read . Length ;
56- var headers = headRows . ToDictionary ( x => x . Value , x => x . Key ) ;
57- var rowValues = read . Select ( ( x , i ) => new { Key = headRows [ i ] , Value = x } ) . ToDictionary ( x => x . Key , x => ( object ) x . Value ) ;
58- throw new Exceptions . ExcelColumnNotFoundException ( null ,
59- headRows [ colIndex ] , null , rowIndex , headers , rowValues , $ "Csv read error, Column: { colIndex } not found in Row: { rowIndex } ") ;
60- }
54+ // invalid row check
55+ if ( read . Length < headRows . Count )
56+ {
57+ var colIndex = read . Length ;
58+ var headers = headRows . ToDictionary ( x => x . Value , x => x . Key ) ;
59+ var rowValues = read
60+ . Select ( ( x , i ) => new KeyValuePair < string , object > ( headRows [ i ] , x ) )
61+ . ToDictionary ( x => x . Key , x => x . Value ) ;
62+
63+ throw new ExcelColumnNotFoundException ( columnIndex : null , headRows [ colIndex ] , null , rowIndex , headers , rowValues , $ "Csv read error: Column { colIndex } not found in Row { rowIndex } ") ;
64+ }
6165
62- //header
63- if ( useHeaderRow )
66+ //header
67+ if ( useHeaderRow )
68+ {
69+ if ( firstRow )
6470 {
65- if ( firstRow )
66- {
67- firstRow = false ;
68- for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
69- headRows . Add ( i , read [ i ] ) ;
70- continue ;
71- }
72-
73- var cell = CustomPropertyHelper . GetEmptyExpandoObject ( headRows ) ;
71+ firstRow = false ;
7472 for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
75- cell [ headRows [ i ] ] = read [ i ] ;
76-
77- yield return cell ;
73+ headRows . Add ( i , read [ i ] ) ;
7874 continue ;
7975 }
8076
77+ var headCell = CustomPropertyHelper . GetEmptyExpandoObject ( headRows ) ;
78+ for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
79+ headCell [ headRows [ i ] ] = read [ i ] ;
8180
82- //body
83- {
84- // record first row as reference
85- if ( firstRow )
86- {
87- firstRow = false ;
88- for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
89- headRows . Add ( i , $ "c{ i + 1 } ") ;
90- }
81+ yield return headCell ;
82+ continue ;
83+ }
9184
92- var cell = CustomPropertyHelper . GetEmptyExpandoObject ( read . Length - 1 , 0 ) ;
93- if ( _config . ReadEmptyStringAsNull )
94- {
95- for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
96- cell [ ColumnHelper . GetAlphabetColumnName ( i ) ] = read [ i ] ? . Length == 0 ? null : read [ i ] ;
97- }
98- else
99- {
100- for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
101- cell [ ColumnHelper . GetAlphabetColumnName ( i ) ] = read [ i ] ;
102- }
85+ //body
86+ if ( firstRow ) // record first row as reference
87+ {
88+ firstRow = false ;
89+ for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
90+ headRows . Add ( i , $ "c{ i + 1 } ") ;
91+ }
10392
104- yield return cell ;
105- }
93+ var cell = CustomPropertyHelper . GetEmptyExpandoObject ( read . Length - 1 , 0 ) ;
94+ if ( _config . ReadEmptyStringAsNull )
95+ {
96+ for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
97+ cell [ ColumnHelper . GetAlphabetColumnName ( i ) ] = read [ i ] ? . Length == 0 ? null : read [ i ] ;
10698 }
99+ else
100+ {
101+ for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
102+ cell [ ColumnHelper . GetAlphabetColumnName ( i ) ] = read [ i ] ;
103+ }
104+
105+ yield return cell ;
107106 }
108107 }
109108 public IEnumerable < T > Query < T > ( string sheetName , string startCell , bool hasHeader ) where T : class , new ( )
110109 {
111- return ExcelOpenXmlSheetReader . QueryImpl < T > ( Query ( false , sheetName , startCell ) , startCell , hasHeader , _config ) ;
110+ var dynamicRecords = Query ( false , sheetName , startCell ) ;
111+ return ExcelOpenXmlSheetReader . QueryImpl < T > ( dynamicRecords , startCell , hasHeader , _config ) ;
112112 }
113113
114114 private string [ ] Split ( string row )
@@ -119,9 +119,10 @@ private string[] Split(string row)
119119 }
120120 else
121121 {
122- return Regex . Split ( row , $ "[\t { _config . Seperator } ](?=(?:[^\" ]|\" [^\" ]*\" )*$)")
123- . Select ( s => Regex . Replace ( s . Replace ( "\" \" " , "\" " ) , "^\" |\" $" , "" ) ) . ToArray ( ) ;
124122 //this code from S.O : https://stackoverflow.com/a/11365961/9131476
123+ return Regex . Split ( row , $ "[\t { _config . Seperator } ](?=(?:[^\" ]|\" [^\" ]*\" )*$)")
124+ . Select ( s => Regex . Replace ( s . Replace ( "\" \" " , "\" " ) , "^\" |\" $" , "" ) )
125+ . ToArray ( ) ;
125126 }
126127 }
127128
@@ -143,56 +144,55 @@ public void Dispose()
143144 public IEnumerable < IDictionary < string , object > > QueryRange ( bool useHeaderRow , string sheetName , string startCell , string endCell )
144145 {
145146 if ( startCell != "A1" )
146- throw new NotImplementedException ( "CSV not Implement startCell" ) ;
147+ throw new NotImplementedException ( "CSV does not implement parameter startCell" ) ;
148+
147149 if ( _stream . CanSeek )
148150 _stream . Position = 0 ;
151+
149152 var reader = _config . StreamReaderFunc ( _stream ) ;
153+
154+ string row ;
155+ var firstRow = true ;
156+ var headRows = new Dictionary < int , string > ( ) ;
157+
158+ while ( ( row = reader . ReadLine ( ) ) != null )
150159 {
151- var row = string . Empty ;
152- string [ ] read ;
153- var firstRow = true ;
154- Dictionary < int , string > headRows = new Dictionary < int , string > ( ) ;
155- while ( ( row = reader . ReadLine ( ) ) != null )
156- {
157- read = Split ( row ) ;
160+ var read = Split ( row ) ;
158161
159- //header
160- if ( useHeaderRow )
162+ //header
163+ if ( useHeaderRow )
164+ {
165+ if ( firstRow )
161166 {
162- if ( firstRow )
163- {
164- firstRow = false ;
165- for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
166- headRows . Add ( i , read [ i ] ) ;
167- continue ;
168- }
169-
170- var cell = CustomPropertyHelper . GetEmptyExpandoObject ( headRows ) ;
167+ firstRow = false ;
171168 for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
172- cell [ headRows [ i ] ] = read [ i ] ;
173-
174- yield return cell ;
169+ headRows . Add ( i , read [ i ] ) ;
175170 continue ;
176171 }
177172
173+ var headCell = CustomPropertyHelper . GetEmptyExpandoObject ( headRows ) ;
174+ for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
175+ headCell [ headRows [ i ] ] = read [ i ] ;
178176
179- //body
180- {
181- var cell = CustomPropertyHelper . GetEmptyExpandoObject ( read . Length - 1 , 0 ) ;
182- for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
183- cell [ ColumnHelper . GetAlphabetColumnName ( i ) ] = read [ i ] ;
184- yield return cell ;
185- }
177+ yield return headCell ;
178+ continue ;
186179 }
180+
181+ //body
182+ var cell = CustomPropertyHelper . GetEmptyExpandoObject ( read . Length - 1 , 0 ) ;
183+ for ( int i = 0 ; i <= read . Length - 1 ; i ++ )
184+ cell [ ColumnHelper . GetAlphabetColumnName ( i ) ] = read [ i ] ;
185+
186+ yield return cell ;
187187 }
188188 }
189189 public IEnumerable < T > QueryRange < T > ( string sheetName , string startCell , string endCel ) where T : class , new ( )
190190 {
191191 return ExcelOpenXmlSheetReader . QueryImplRange < T > ( QueryRange ( false , sheetName , startCell , endCel ) , startCell , endCel , this . _config ) ;
192192 }
193- public Task < IEnumerable < IDictionary < string , object > > > QueryAsyncRange ( bool UseHeaderRow , string sheetName , string startCell , string endCel , CancellationToken cancellationToken = default )
193+ public Task < IEnumerable < IDictionary < string , object > > > QueryAsyncRange ( bool useHeaderRow , string sheetName , string startCell , string endCel , CancellationToken cancellationToken = default )
194194 {
195- return Task . Run ( ( ) => QueryRange ( UseHeaderRow , sheetName , startCell , endCel ) , cancellationToken ) ;
195+ return Task . Run ( ( ) => QueryRange ( useHeaderRow , sheetName , startCell , endCel ) , cancellationToken ) ;
196196 }
197197
198198 public Task < IEnumerable < T > > QueryAsyncRange < T > ( string sheetName , string startCell , string endCel , bool hasHeader , CancellationToken cancellationToken = default ) where T : class , new ( )
0 commit comments