mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-11-29 18:38:08 +08:00
0.14.0
- [New] Query、GetColumns support startCell #147 - [New] GetColumns support read headers
This commit is contained in:
parent
10faf5d2d5
commit
33c3a191cc
@ -239,6 +239,14 @@ var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);
|
||||
|
||||
|
||||
|
||||
#### 10. Specify the cell to start reading data
|
||||
|
||||
```csharp
|
||||
MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
|
||||
```
|
||||
|
||||
![image](https://user-images.githubusercontent.com/12729184/117260316-8593c400-ae81-11eb-9877-c087b7ac2b01.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -241,6 +241,16 @@ var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);
|
||||
|
||||
![image](https://user-images.githubusercontent.com/12729184/116673475-07917200-a9d6-11eb-947e-a6f68cce58df.png)
|
||||
|
||||
#### 10. 指定单元格开始读取数据
|
||||
|
||||
```csharp
|
||||
MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
|
||||
```
|
||||
|
||||
![image](https://user-images.githubusercontent.com/12729184/117260316-8593c400-ae81-11eb-9877-c087b7ac2b01.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 写 Excel <a name="getstart2"></a>
|
||||
|
@ -243,6 +243,16 @@ var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);
|
||||
|
||||
![image](https://user-images.githubusercontent.com/12729184/116673475-07917200-a9d6-11eb-947e-a6f68cce58df.png)
|
||||
|
||||
#### 10. 指定單元格開始讀取資料
|
||||
|
||||
```csharp
|
||||
MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
|
||||
```
|
||||
|
||||
![image](https://user-images.githubusercontent.com/12729184/117260316-8593c400-ae81-11eb-9877-c087b7ac2b01.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 寫 Excel <a name="getstart2"></a>
|
||||
|
Binary file not shown.
Binary file not shown.
@ -6,6 +6,10 @@
|
||||
|
||||
---
|
||||
|
||||
### 0.14.0
|
||||
- [New] Query、GetColumns support startCell #147
|
||||
- [New] GetColumns support read headers
|
||||
|
||||
### 0.13.5
|
||||
- [New] Support QueryAsDataTable method [#216](https://github.com/shps951023/MiniExcel/issues/216)
|
||||
- [New] SaveAs support IDataReader value parameter [#211](https://github.com/shps951023/MiniExcel/issues/211)
|
||||
|
@ -7,6 +7,10 @@
|
||||
|
||||
---
|
||||
|
||||
### 0.14.0
|
||||
- [New] Query、GetColumns 支持 startCell 能指定 cell 开始读取数据 #147
|
||||
- [New] GetColumns 支持读取表头
|
||||
|
||||
### 0.13.5
|
||||
- [New] 新增 QueryAsDataTable 方法 [#216](https://github.com/shps951023/MiniExcel/issues/216)
|
||||
- [New] SaveAs 支持 IDataReader value 参数 [#211](https://github.com/shps951023/MiniExcel/issues/211)
|
||||
|
@ -7,6 +7,10 @@
|
||||
|
||||
---
|
||||
|
||||
### 0.14.0
|
||||
- [New] Query、GetColumns 支持 startCell 能指定 cell 開始讀取資料 #147
|
||||
- [New] GetColumns 支持讀取表頭
|
||||
|
||||
### 0.13.5
|
||||
- [New] 新增 QueryAsDataTable 方法 [#216](https://github.com/shps951023/MiniExcel/issues/216)
|
||||
- [New] SaveAs 支持 IDataReader value 參數 [#211](https://github.com/shps951023/MiniExcel/issues/211)
|
||||
|
BIN
samples/xlsx/TestIssue147.xlsx
Normal file
BIN
samples/xlsx/TestIssue147.xlsx
Normal file
Binary file not shown.
BIN
samples/xlsx/TestStartCellQuery.xlsx
Normal file
BIN
samples/xlsx/TestStartCellQuery.xlsx
Normal file
Binary file not shown.
@ -14,8 +14,10 @@ namespace MiniExcelLibs.Csv
|
||||
{
|
||||
this._stream = stream;
|
||||
}
|
||||
public IEnumerable<IDictionary<string, object>> Query(bool useHeaderRow, string sheetName, IConfiguration configuration)
|
||||
public IEnumerable<IDictionary<string, object>> Query(bool useHeaderRow, string sheetName, string startCell, IConfiguration configuration)
|
||||
{
|
||||
if (startCell != "A1")
|
||||
throw new NotImplementedException("CSV not Implement startCell");
|
||||
var cf = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration;
|
||||
|
||||
using (var reader = cf.GetStreamReaderFunc(_stream))
|
||||
@ -52,7 +54,7 @@ namespace MiniExcelLibs.Csv
|
||||
|
||||
//body
|
||||
{
|
||||
var cell = Helpers.GetEmptyExpandoObject(read.Length - 1);
|
||||
var cell = Helpers.GetEmptyExpandoObject(read.Length - 1,0);
|
||||
for (int i = 0; i <= read.Length - 1; i++)
|
||||
cell[Helpers.GetAlphabetColumnName(i)] = read[i];
|
||||
yield return cell;
|
||||
@ -61,7 +63,7 @@ namespace MiniExcelLibs.Csv
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<T> Query<T>(string sheetName, IConfiguration configuration) where T : class, new()
|
||||
public IEnumerable<T> Query<T>(string sheetName, string startCell, IConfiguration configuration) where T : class, new()
|
||||
{
|
||||
var cf = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration;
|
||||
|
||||
|
@ -4,7 +4,7 @@ namespace MiniExcelLibs
|
||||
{
|
||||
internal interface IExcelReader
|
||||
{
|
||||
IEnumerable<IDictionary<string, object>> Query(bool UseHeaderRow, string sheetName, IConfiguration configuration);
|
||||
IEnumerable<T> Query<T>(string sheetName, IConfiguration configuration) where T : class, new();
|
||||
IEnumerable<IDictionary<string, object>> Query(bool UseHeaderRow, string sheetName,string startCell, IConfiguration configuration);
|
||||
IEnumerable<T> Query<T>(string sheetName, string startCell, IConfiguration configuration) where T : class, new();
|
||||
}
|
||||
}
|
||||
|
@ -28,28 +28,28 @@
|
||||
ExcelWriterFactory.GetProvider(stream, excelType).SaveAs(value, sheetName, printHeader, configuration);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Query<T>(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null) where T : class, new()
|
||||
public static IEnumerable<T> Query<T>(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new()
|
||||
{
|
||||
using (var stream = Helpers.OpenSharedRead(path))
|
||||
foreach (var item in Query<T>(stream, sheetName, GetExcelType(path, excelType), configuration))
|
||||
foreach (var item in Query<T>(stream, sheetName, GetExcelType(path, excelType), startCell, configuration))
|
||||
yield return item; //Foreach yield return twice reason : https://stackoverflow.com/questions/66791982/ienumerable-extract-code-lazy-loading-show-stream-was-not-readable
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Query<T>(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null) where T : class, new()
|
||||
public static IEnumerable<T> Query<T>(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new()
|
||||
{
|
||||
return ExcelReaderFactory.GetProvider(stream, GetExcelType(stream, excelType)).Query<T>(sheetName, configuration);
|
||||
return ExcelReaderFactory.GetProvider(stream, GetExcelType(stream, excelType)).Query<T>(sheetName, startCell, configuration);
|
||||
}
|
||||
|
||||
public static IEnumerable<dynamic> Query(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null)
|
||||
public static IEnumerable<dynamic> Query(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
using (var stream = Helpers.OpenSharedRead(path))
|
||||
foreach (var item in Query(stream, useHeaderRow, sheetName, GetExcelType(path, excelType), configuration))
|
||||
foreach (var item in Query(stream, useHeaderRow, sheetName, GetExcelType(path, excelType), startCell, configuration))
|
||||
yield return item;
|
||||
}
|
||||
|
||||
public static IEnumerable<dynamic> Query(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null)
|
||||
public static IEnumerable<dynamic> Query(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
return ExcelReaderFactory.GetProvider(stream, GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName, configuration);
|
||||
return ExcelReaderFactory.GetProvider(stream, GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName, startCell, configuration);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetSheetNames(string path)
|
||||
@ -66,15 +66,15 @@
|
||||
yield return item.Name;
|
||||
}
|
||||
|
||||
public static ICollection<string> GetColumns(string path)
|
||||
public static ICollection<string> GetColumns(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
using (var stream = Helpers.OpenSharedRead(path))
|
||||
return (Query(stream).FirstOrDefault() as IDictionary<string, object>)?.Keys;
|
||||
return GetColumns(stream, useHeaderRow, sheetName, excelType, startCell, configuration);
|
||||
}
|
||||
|
||||
public static ICollection<string> GetColumns(this Stream stream)
|
||||
public static ICollection<string> GetColumns(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
return (Query(stream).FirstOrDefault() as IDictionary<string, object>)?.Keys;
|
||||
return (Query(stream, useHeaderRow,sheetName,excelType,startCell,configuration).FirstOrDefault() as IDictionary<string, object>)?.Keys;
|
||||
}
|
||||
|
||||
public static void SaveAsByTemplate(string path, string templatePath, object value)
|
||||
@ -106,21 +106,21 @@
|
||||
/// <summary>
|
||||
/// This method is not recommended, because it'll load all data into memory.
|
||||
/// </summary>
|
||||
public static DataTable QueryAsDataTable(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null)
|
||||
public static DataTable QueryAsDataTable(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
using (var stream = Helpers.OpenSharedRead(path))
|
||||
return QueryAsDataTable(stream, useHeaderRow, sheetName, GetExcelType(path, excelType), configuration);
|
||||
return QueryAsDataTable(stream, useHeaderRow, sheetName, GetExcelType(path, excelType), startCell, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is not recommended, because it'll load all data into memory.
|
||||
/// </summary>
|
||||
public static DataTable QueryAsDataTable(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null)
|
||||
public static DataTable QueryAsDataTable(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
var dt = new DataTable();
|
||||
dt.TableName = sheetName;
|
||||
var first = true;
|
||||
var rows = ExcelReaderFactory.GetProvider(stream, GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName, configuration);
|
||||
var rows = ExcelReaderFactory.GetProvider(stream, GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName,startCell, configuration);
|
||||
foreach (IDictionary<string, object> row in rows)
|
||||
{
|
||||
if (first)
|
||||
|
@ -31,8 +31,12 @@ namespace MiniExcelLibs.OpenXml
|
||||
_archive = new ExcelOpenXmlZip(stream);
|
||||
}
|
||||
|
||||
public IEnumerable<IDictionary<string, object>> Query(bool UseHeaderRow, string sheetName, IConfiguration configuration)
|
||||
public IEnumerable<IDictionary<string, object>> Query(bool useHeaderRow, string sheetName, string startCell, IConfiguration configuration)
|
||||
{
|
||||
if (!ReferenceHelper.ParseReference(startCell, out var startColumnIndex, out var startRowIndex))
|
||||
throw new InvalidDataException($"startCell {startCell} is Invalid");
|
||||
startColumnIndex--; startRowIndex--;
|
||||
|
||||
//TODO:need to optimize
|
||||
SetSharedStrings();
|
||||
|
||||
@ -64,9 +68,9 @@ namespace MiniExcelLibs.OpenXml
|
||||
var maxRowIndex = -1;
|
||||
var maxColumnIndex = -1;
|
||||
|
||||
//TODO: merge one open read
|
||||
using (var firstSheetEntryStream = sheetEntry.Open())
|
||||
using (XmlReader reader = XmlReader.Create(firstSheetEntryStream, _xmlSettings))
|
||||
//Q. why need 3 times openstream merge one open read? A. no, zipstream can't use position = 0
|
||||
using (var sheetStream = sheetEntry.Open())
|
||||
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
@ -111,8 +115,8 @@ namespace MiniExcelLibs.OpenXml
|
||||
|
||||
if (withoutCR)
|
||||
{
|
||||
using (var firstSheetEntryStream = sheetEntry.Open())
|
||||
using (XmlReader reader = XmlReader.Create(firstSheetEntryStream, _xmlSettings))
|
||||
using (var sheetStream = sheetEntry.Open())
|
||||
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
|
||||
{
|
||||
if (!reader.IsStartElement("worksheet", _ns))
|
||||
yield break;
|
||||
@ -167,8 +171,10 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
|
||||
|
||||
using (var firstSheetEntryStream = sheetEntry.Open())
|
||||
using (XmlReader reader = XmlReader.Create(firstSheetEntryStream, _xmlSettings))
|
||||
|
||||
|
||||
using (var sheetStream = sheetEntry.Open())
|
||||
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
|
||||
{
|
||||
if (!reader.IsStartElement("worksheet", _ns))
|
||||
yield break;
|
||||
@ -186,6 +192,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
Dictionary<int, string> headRows = new Dictionary<int, string>();
|
||||
int rowIndex = -1;
|
||||
int nextRowIndex = 0;
|
||||
bool isFirstRow = true;
|
||||
while (!reader.EOF)
|
||||
{
|
||||
if (reader.IsStartElement("row", _ns))
|
||||
@ -195,34 +202,45 @@ namespace MiniExcelLibs.OpenXml
|
||||
rowIndex = arValue - 1; // The row attribute is 1-based
|
||||
else
|
||||
rowIndex++;
|
||||
|
||||
// row -> c
|
||||
if (!XmlReaderHelper.ReadFirstContent(reader))
|
||||
continue;
|
||||
|
||||
// startcell pass rows
|
||||
if (rowIndex < startRowIndex )
|
||||
{
|
||||
XmlReaderHelper.SkipToNextSameLevelDom(reader);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// fill empty rows
|
||||
if(!(nextRowIndex < startRowIndex))
|
||||
{
|
||||
if (nextRowIndex < rowIndex)
|
||||
{
|
||||
for (int i = nextRowIndex; i < rowIndex; i++)
|
||||
if (UseHeaderRow)
|
||||
yield return Helpers.GetEmptyExpandoObject(headRows);
|
||||
else
|
||||
yield return Helpers.GetEmptyExpandoObject(maxColumnIndex);
|
||||
{
|
||||
yield return GetCell(useHeaderRow, maxColumnIndex, headRows,startColumnIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Cells
|
||||
{
|
||||
var cell = UseHeaderRow ? Helpers.GetEmptyExpandoObject(headRows) : Helpers.GetEmptyExpandoObject(maxColumnIndex);
|
||||
var cell = GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex);
|
||||
var columnIndex = withoutCR ? -1 : 0;
|
||||
while (!reader.EOF)
|
||||
{
|
||||
if (reader.IsStartElement("c", _ns))
|
||||
{
|
||||
var aS = reader.GetAttribute("s");
|
||||
var cellValue = ReadCell(reader, columnIndex, withoutCR, out var _columnIndex);
|
||||
columnIndex = _columnIndex;
|
||||
var cellValue = ReadCellAndSetColumnIndex(reader, ref columnIndex, withoutCR,startColumnIndex);
|
||||
|
||||
if (columnIndex < startColumnIndex)
|
||||
continue;
|
||||
|
||||
// TODO: bad code smell
|
||||
if (!string.IsNullOrEmpty(aS)) // if c with s meaning is custom style need to check type by xl/style.xml
|
||||
{
|
||||
int xfIndex = -1;
|
||||
@ -232,59 +250,27 @@ namespace MiniExcelLibs.OpenXml
|
||||
// only when have s attribute then load styles xml data
|
||||
if (_style == null)
|
||||
_style = new ExcelOpenXmlStyles(_archive);
|
||||
//if not using First Head then using 1,2,3 as index
|
||||
if (UseHeaderRow)
|
||||
{
|
||||
if (rowIndex == 0)
|
||||
{
|
||||
var customStyleCellValue = _style.ConvertValueByStyleFormat(xfIndex, cellValue)?.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(customStyleCellValue))
|
||||
headRows.Add(columnIndex, customStyleCellValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (headRows.ContainsKey(columnIndex))
|
||||
{
|
||||
var key = headRows[columnIndex];
|
||||
cell[key] = _style.ConvertValueByStyleFormat(xfIndex, cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if not using First Head then using A,B,C as index
|
||||
cell[Helpers.GetAlphabetColumnName(columnIndex)] = _style.ConvertValueByStyleFormat(xfIndex, cellValue);
|
||||
}
|
||||
|
||||
var customStyleCellValue = _style.ConvertValueByStyleFormat(xfIndex, cellValue)?.ToString();
|
||||
cellValue = _style.ConvertValueByStyleFormat(xfIndex, cellValue);
|
||||
SetCellsValueAndHeaders(customStyleCellValue, cellValue, useHeaderRow, ref headRows, ref isFirstRow, ref cell, columnIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UseHeaderRow)
|
||||
{
|
||||
if (rowIndex == 0)
|
||||
{
|
||||
var valueString = cellValue?.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(valueString))
|
||||
headRows.Add(columnIndex, valueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (headRows.ContainsKey(columnIndex))
|
||||
cell[headRows[columnIndex]] = cellValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if not using First Head then using A,B,C as index
|
||||
cell[Helpers.GetAlphabetColumnName(columnIndex)] = cellValue;
|
||||
}
|
||||
SetCellsValueAndHeaders(cellValue?.ToString(), cellValue, useHeaderRow, ref headRows, ref isFirstRow, ref cell, columnIndex);
|
||||
}
|
||||
}
|
||||
else if (!XmlReaderHelper.SkipContent(reader))
|
||||
break;
|
||||
}
|
||||
|
||||
if (UseHeaderRow && rowIndex == 0)
|
||||
continue;
|
||||
if (isFirstRow)
|
||||
{
|
||||
isFirstRow = false; // for startcell logic
|
||||
if (useHeaderRow)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
yield return cell;
|
||||
}
|
||||
@ -304,15 +290,44 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> GetCell(bool useHeaderRow, int maxColumnIndex, Dictionary<int, string> headRows, int startColumnIndex)
|
||||
{
|
||||
return useHeaderRow ? Helpers.GetEmptyExpandoObject(headRows) : Helpers.GetEmptyExpandoObject(maxColumnIndex, startColumnIndex);
|
||||
}
|
||||
|
||||
public IEnumerable<T> Query<T>(string sheetName, IConfiguration configuration) where T : class, new()
|
||||
private void SetCellsValueAndHeaders(string cellValueString,object cellValue, bool useHeaderRow, ref Dictionary<int, string> headRows, ref bool isFirstRow, ref IDictionary<string, object> cell, int columnIndex)
|
||||
{
|
||||
if (useHeaderRow)
|
||||
{
|
||||
if (isFirstRow) // for startcell logic
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(cellValueString))
|
||||
headRows.Add(columnIndex, cellValueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (headRows.ContainsKey(columnIndex))
|
||||
{
|
||||
var key = headRows[columnIndex];
|
||||
cell[key] = cellValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if not using First Head then using A,B,C as index
|
||||
cell[Helpers.GetAlphabetColumnName(columnIndex)] = cellValue;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<T> Query<T>(string sheetName, string startCell, IConfiguration configuration) where T : class, new()
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
var first = true;
|
||||
List<ExcelCustomPropertyInfo> props = null;
|
||||
var headers = Query(false, sheetName, configuration).FirstOrDefault()?.Values?.Select(s => s?.ToString())?.ToArray(); //TODO:need to optimize
|
||||
foreach (var item in Query(true, sheetName, configuration))
|
||||
var headers = Query(false, sheetName, startCell, configuration).FirstOrDefault()?.Values?.Select(s => s?.ToString())?.ToArray(); //TODO:need to optimize
|
||||
foreach (var item in Query(true, sheetName, startCell, configuration))
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
@ -510,19 +525,32 @@ namespace MiniExcelLibs.OpenXml
|
||||
return sheetRecords;
|
||||
}
|
||||
|
||||
private object ReadCell(XmlReader reader, int nextColumnIndex, bool withoutCR, out int columnIndex)
|
||||
private object ReadCellAndSetColumnIndex(XmlReader reader,ref int columnIndex, bool withoutCR,int startColumnIndex)
|
||||
{
|
||||
var newColumnIndex = 0;
|
||||
int xfIndex = -1;
|
||||
var aT = reader.GetAttribute("t");
|
||||
var aR = reader.GetAttribute("r");
|
||||
|
||||
if (withoutCR)
|
||||
columnIndex = nextColumnIndex + 1;
|
||||
newColumnIndex = columnIndex + 1;
|
||||
//TODO:need to check only need nextColumnIndex or columnIndex
|
||||
else if (ReferenceHelper.ParseReference(aR, out int referenceColumn, out _))
|
||||
columnIndex = referenceColumn - 1; // ParseReference is 1-based
|
||||
newColumnIndex = referenceColumn - 1; // ParseReference is 1-based
|
||||
else
|
||||
columnIndex = nextColumnIndex;
|
||||
newColumnIndex = columnIndex;
|
||||
|
||||
columnIndex = newColumnIndex;
|
||||
|
||||
if (columnIndex < startColumnIndex)
|
||||
{
|
||||
if (!XmlReaderHelper.ReadFirstContent(reader))
|
||||
return null;
|
||||
while (!reader.EOF)
|
||||
if (!XmlReaderHelper.SkipContent(reader))
|
||||
break;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!XmlReaderHelper.ReadFirstContent(reader))
|
||||
return null;
|
||||
@ -548,7 +576,9 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void ConvertCellValue(string rawValue, string aT, int xfIndex, out object value)
|
||||
|
@ -72,12 +72,12 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static IDictionary<string, object> GetEmptyExpandoObject(int maxColumnIndex)
|
||||
internal static IDictionary<string, object> GetEmptyExpandoObject(int maxColumnIndex,int startCellIndex)
|
||||
{
|
||||
// TODO: strong type mapping can ignore this
|
||||
// TODO: it can recode better performance
|
||||
var cell = (IDictionary<string, object>)new ExpandoObject();
|
||||
for (int i = 0; i <= maxColumnIndex; i++)
|
||||
for (int i = startCellIndex; i <= maxColumnIndex; i++)
|
||||
{
|
||||
var key = GetAlphabetColumnName(i);
|
||||
if (!cell.ContainsKey(key))
|
||||
|
@ -1,34 +1,56 @@
|
||||
/**
|
||||
This Class Modified from ExcelDataReader : https://github.com/ExcelDataReader/ExcelDataReader
|
||||
**/
|
||||
namespace MiniExcelLibs.Utils
|
||||
namespace MiniExcelLibs.Utils
|
||||
{
|
||||
using System.Xml;
|
||||
|
||||
internal static class XmlReaderHelper
|
||||
{
|
||||
public static bool ReadFirstContent(XmlReader xmlReader)
|
||||
/// <summary>
|
||||
/// Pass <?xml> and <worksheet>
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
public static void PassXmlDeclartionAndWorksheet(this XmlReader reader)
|
||||
{
|
||||
if (xmlReader.IsEmptyElement)
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// e.g skip row 1 to row 2
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
public static void SkipToNextSameLevelDom(XmlReader reader)
|
||||
{
|
||||
while (!reader.EOF)
|
||||
{
|
||||
xmlReader.Read();
|
||||
if (!XmlReaderHelper.SkipContent(reader))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Method from ExcelDataReader : https://github.com/ExcelDataReader/ExcelDataReader
|
||||
public static bool ReadFirstContent(XmlReader reader)
|
||||
{
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
reader.Read();
|
||||
return false;
|
||||
}
|
||||
|
||||
xmlReader.MoveToContent();
|
||||
xmlReader.Read();
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool SkipContent(XmlReader xmlReader)
|
||||
//Method from ExcelDataReader : https://github.com/ExcelDataReader/ExcelDataReader
|
||||
public static bool SkipContent(XmlReader reader)
|
||||
{
|
||||
if (xmlReader.NodeType == XmlNodeType.EndElement)
|
||||
if (reader.NodeType == XmlNodeType.EndElement)
|
||||
{
|
||||
xmlReader.Read();
|
||||
reader.Read();
|
||||
return false;
|
||||
}
|
||||
|
||||
xmlReader.Skip();
|
||||
reader.Skip();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,53 @@ namespace MiniExcelLibs.Tests
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query Support StartCell #147
|
||||
/// https://github.com/shps951023/MiniExcel/issues/147
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Issue147()
|
||||
{
|
||||
{
|
||||
var path = PathHelper.GetSamplePath("xlsx/TestIssue147.xlsx");
|
||||
var rows = MiniExcel.Query(path, useHeaderRow: false, startCell: "C3", sheetName: "Sheet1").ToList();
|
||||
|
||||
Assert.Equal(new[] { "C", "D", "E" }, (rows[0] as IDictionary<string, object>).Keys);
|
||||
Assert.Equal(new[] { "Column1", "Column2", "Column3" }, new[] { rows[0].C as string, rows[0].D as string, rows[0].E as string });
|
||||
Assert.Equal(new[] { "C4", "D4", "E4" }, new[] { rows[1].C as string, rows[1].D as string, rows[1].E as string });
|
||||
Assert.Equal(new[] { "C9", "D9", "E9" }, new[] { rows[6].C as string, rows[6].D as string, rows[6].E as string });
|
||||
Assert.Equal(new[] { "C12", "D12", "E12" }, new[] { rows[9].C as string, rows[9].D as string, rows[9].E as string });
|
||||
Assert.Equal(new[] { "C13", "D13", "E13" }, new[] { rows[10].C as string, rows[10].D as string, rows[10].E as string });
|
||||
foreach (var i in new[] { 4, 5, 7, 8 })
|
||||
Assert.Equal(new[] { default(string), default(string), default(string) }, new[] { rows[i].C as string, rows[i].D as string, rows[i].E as string });
|
||||
|
||||
Assert.Equal(11, rows.Count);
|
||||
|
||||
|
||||
var columns = MiniExcel.GetColumns(path, startCell: "C3");
|
||||
Assert.Equal(new[] { "C", "D", "E" }, columns);
|
||||
}
|
||||
|
||||
{
|
||||
var path = PathHelper.GetSamplePath("xlsx/TestIssue147.xlsx");
|
||||
var rows = MiniExcel.Query(path, useHeaderRow: true, startCell: "C3", sheetName: "Sheet1").ToList();
|
||||
|
||||
Assert.Equal(new[] { "Column1", "Column2", "Column3" }, (rows[0] as IDictionary<string, object>).Keys);
|
||||
Assert.Equal(new[] { "C4", "D4", "E4" }, new[] { rows[0].Column1 as string, rows[0].Column2 as string, rows[0].Column3 as string });
|
||||
Assert.Equal(new[] { "C9", "D9", "E9" }, new[] { rows[5].Column1 as string, rows[5].Column2 as string, rows[5].Column3 as string });
|
||||
Assert.Equal(new[] { "C12", "D12", "E12" }, new[] { rows[8].Column1 as string, rows[8].Column2 as string, rows[8].Column3 as string });
|
||||
Assert.Equal(new[] { "C13", "D13", "E13" }, new[] { rows[9].Column1 as string, rows[9].Column2 as string, rows[9].Column3 as string });
|
||||
foreach (var i in new[] { 3, 4, 6, 7 })
|
||||
Assert.Equal(new[] { default(string), default(string), default(string) }, new[] { rows[i].Column1 as string, rows[i].Column2 as string, rows[i].Column3 as string });
|
||||
|
||||
Assert.Equal(10, rows.Count);
|
||||
|
||||
|
||||
var columns = MiniExcel.GetColumns(path, useHeaderRow: true, startCell: "C3");
|
||||
Assert.Equal(new[] { "Column1", "Column2", "Column3" }, columns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [Can SaveAs support iDataReader export to avoid the dataTable consuming too much memory · Issue #211 · shps951023/MiniExcel]
|
||||
|
Loading…
Reference in New Issue
Block a user