任务
从 excel
导入数据至 DataTable
问题
不包含任何数据的单元格将被跳过,并且行中具有数据的下一个单元格用作空列的值。 例如
A1 为空 A2 的值为 Tom
然后在导入数据时A1
获取 A2 的值并且 A2 保持为空
为了清楚起见,我在下面提供了一些屏幕截图
这是excel数据
代码
public class ImportExcelOpenXml
{
public static DataTable Fill_dataTable(string fileName)
{
DataTable dt = new DataTable();
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
foreach (Cell cell in rows.ElementAt(0))
{
dt.Columns.Add(GetCellValue(spreadSheetDocument, cell));
}
foreach (Row row in rows) //this will also include your header row...
{
DataRow tempRow = dt.NewRow();
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
}
dt.Rows.Add(tempRow);
}
}
dt.Rows.RemoveAt(0); //...so i'm taking it out here.
return dt;
}
public static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
else
{
return value;
}
}
}
我的想法
我觉得有问题
public IEnumerable<T> Descendants<T>() where T : OpenXmlElement;
如果我想使用 Descendants
计算列数
IEnumerable<Row> rows = sheetData.Descendants<<Row>();
int colCnt = rows.ElementAt(0).Count();
或
如果我使用 Descendants
获取行数
IEnumerable<Row> rows = sheetData.Descendants<<Row>();
int rowCnt = rows.Count();`
在这两种情况下 Descendants
正在跳过空单元格
有没有Descendants
的替代品? .
非常感谢您的建议
P.S:我也曾考虑过使用像 A1、A2 这样的列名来获取单元格值,但为了做到这一点,我必须获得列和行的确切数量,这不是可能通过使用 Descendants
功能。
最佳答案
如果一行的所有单元格中都有一些数据,那么一切正常。但是,如果您碰巧连续有一个空单元格,那么事情就会变得一团糟。
为什么它首先发生?
原因在于下面这行代码:
row.Descendants<Cell>().Count()
Count()
函数为您提供行中非空单元格的数量,即它会在返回计数时忽略所有空单元格。所以,当你通过 row.Descendants<Cell>().ElementAt(i)
作为 GetCellValue
的参数像这样的方法:
GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
然后,它将查找下一个非空 单元格的内容,不一定是列索引i
处单元格的内容例如如果第一列为空,我们调用 ElementAt(1)
,它返回第二列中的值,我们的程序逻辑变得困惑。
解决方案:我们需要处理行中空单元格的出现,即我们需要计算出目标单元格的实际/有效列索引,以防它之前有一些空单元格在给定的行中。所以,你需要替换你的 for
循环代码如下:
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
}
与
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
Cell cell = row.Descendants<Cell>().ElementAt(i);
int actualCellIndex = CellReferenceToIndex(cell);
tempRow[actualCellIndex] = GetCellValue(spreadSheetDocument, cell);
}
此外,在您的代码中添加以下方法,该方法在上述修改后的代码段中用于获取任何单元格的实际/有效列索引:
private static int CellReferenceToIndex(Cell cell)
{
int index = 0;
string reference = cell.CellReference.ToString().ToUpper();
foreach (char ch in reference)
{
if (Char.IsLetter(ch))
{
int value = (int)ch - (int)'A';
index = (index == 0) ? value : ((index + 1) * 26) + value;
}
else
{
return index;
}
}
return index;
}
注意:Excel 行中的索引从 1 开始,这与从 0 开始的各种编程语言不同。
关于C# 打开 XML : empty cells are getting skipped while getting data from EXCEL to DATATABLE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36100011/