使用 Interop 从 excel 获取最后一个非空列和行索引
Get Last non empty column and row index from excel using Interop
我正在尝试使用 Interop Library 从 excel 文件中删除所有多余的空白行和列。
我关注了这个问题 ,我发现它很有帮助。
但我有 excel 个文件,其中包含一小部分数据,但有很多空行和空列 (从最后一个非空行(或列)到工作表)
我尝试循环遍历行和列,但循环需要几个小时。
我正在尝试获取最后一个非空行和列索引,以便我可以在一行中删除整个空范围
XlWks.Range("...").EntireRow.Delete(xlShiftUp)
注意:我正在尝试获取包含数据的最后一行以删除所有多余的空白(在此行或列之后)
有什么建议吗?
注意:代码必须兼容SSIS脚本任务环境
您应该能够找到与此类似的最后一个非空行和列:
with m_XlWrkSheet
lastRow = .UsedRange.Rows.Count
lastCol = .UsedRange.Columns.Count
end with
那是 VB.NET,但它应该或多或少起作用。那将 return 第 16 行和第 10 列(根据您上面的图片)。然后你可以用它来找到你想在一行中全部删除的范围。
我正在使用 ClosedXml,它具有有用的 'LastUsedRow' 和 'LastUsedColumn' 方法。
var wb = new XLWorkbook(@"<path>\test.xlsx", XLEventTracking.Disabled);
var sheet = wb.Worksheet("Sheet1");
for (int i = sheet.LastRowUsed().RowNumber() - 1; i >= 1; i--)
{
var row = sheet.Row(i);
if (row.IsEmpty())
{
row.Delete();
}
}
wb.Save();
这个简单的循环在 38 秒内删除了 10000 行中的 5000 行。不快,但比 'hours' 好很多。这取决于您处理的 rows/columns 数量,当然您没有说。
但是,在对 50000 行中的 25000 行进行进一步测试后,删除循环中的空行确实需要大约 30 分钟。显然删除行不是一个有效的过程。
更好的解决方案是创建一个新的 sheet,然后复制您要保留的行。
第 1 步 - 创建 sheet 50000 行和 20 列,每隔一行和一列为空。
var wb = new XLWorkbook(@"C:\Users\passp\Documents\test.xlsx");
var sheet = wb.Worksheet("Sheet1");
sheet.Clear();
for (int i = 1; i < 50000; i+=2)
{
var row = sheet.Row(i);
for (int j = 1; j < 20; j += 2)
{
row.Cell(j).Value = i * j;
}
}
第 2 步 - 将包含数据的行复制到新的 sheet。这需要 10 秒。
var wb = new XLWorkbook(@"C:\Users\passp\Documents\test.xlsx", XLEventTracking.Disabled);
var sheet = wb.Worksheet("Sheet1");
var sheet2 = wb.Worksheet("Sheet2");
sheet2.Clear();
sheet.RowsUsed()
.Where(r => !r.IsEmpty())
.Select((r, index) => new { Row = r, Index = index + 1} )
.ForEach(r =>
{
var newRow = sheet2.Row(r.Index);
r.Row.CopyTo(newRow);
}
);
wb.Save();
第 3 步 - 这将对列执行相同的操作。
几年前我创建了一个 MSDN 代码示例,允许开发人员从作品中获取最后使用的行和列sheet。我对其进行了修改,将所有需要的代码放入一个带有 windows 表单前端的 class 库中以演示该操作。
底层代码使用Microsoft.Office.Interop.Excel。
Microsoft 一个驱动器上的位置
https://1drv.ms/u/s!AtGAgKKpqdWjiEGdBzWDCSCZAMaM
在这里,我获取 Excel 文件中的第一个 sheet,获取最后使用的行和列,并作为有效的单元格地址显示。
Private Sub cmdAddress1_Click(sender As Object, e As EventArgs) Handles cmdAddress1.Click
Dim ops As New GetExcelColumnLastRowInformation
Dim info = New UsedInformation
ExcelInformationData = info.UsedInformation(FileName, ops.GetSheets(FileName))
Dim SheetName As String = ExcelInformationData.FirstOrDefault.SheetName
Dim cellAddress = (
From item In ExcelInformationData
Where item.SheetName = ExcelInformationData.FirstOrDefault.SheetName
Select item.LastCell).FirstOrDefault
MessageBox.Show($"{SheetName} - {cellAddress}")
End Sub
在演示项目中,我还获取了 excel 文件的所有 sheet,并将它们显示在列表框中。 Select 列表框中的 sheet 名称,并在有效的单元格地址中获取 sheet 的最后一行和最后一列。
Private Sub cmdAddress_Click(sender As Object, e As EventArgs) Handles cmdAddress.Click
Dim cellAddress =
(
From item In ExcelInformationData
Where item.SheetName = ListBox1.Text
Select item.LastCell).FirstOrDefault
If cellAddress IsNot Nothing Then
MessageBox.Show($"{ListBox1.Text} {cellAddress}")
End If
End Sub
从上面的 link 打开解决方案时,乍一看,您会注意到有很多代码。代码是最优的,会立即释放所有对象。
- 要获取最后一个非空 column/row 索引,可以使用 Excel 函数
Find
。参见 GetLastIndexOfNonEmptyCell
。
- 然后Excel工作表函数
CountA
判断单元格是否为空和union整个 rows/columns 到一个 rows/columns 范围。
- 最后一次删除这个范围。
public void Yahfoufi(string excelFile)
{
var exapp = new Microsoft.Office.Interop.Excel.Application {Visible = true};
var wrb = exapp.Workbooks.Open(excelFile);
var sh = wrb.Sheets["Sheet1"];
var lastRow = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByRows);
var lastCol = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByColumns);
var target = sh.Range[sh.Range["A1"], sh.Cells[lastRow, lastCol]];
Range deleteRows = GetEmptyRows(exapp, target);
Range deleteColumns = GetEmptyColumns(exapp, target);
deleteColumns?.Delete();
deleteRows?.Delete();
}
private static int GetLastIndexOfNonEmptyCell(
Microsoft.Office.Interop.Excel.Application app,
Worksheet sheet,
XlSearchOrder searchOrder)
{
Range rng = sheet.Cells.Find(
What: "*",
After: sheet.Range["A1"],
LookIn: XlFindLookIn.xlFormulas,
LookAt: XlLookAt.xlPart,
SearchOrder: searchOrder,
SearchDirection: XlSearchDirection.xlPrevious,
MatchCase: false);
if (rng == null)
return 1;
return searchOrder == XlSearchOrder.xlByRows
? rng.Row
: rng.Column;
}
private static Range GetEmptyRows(
Microsoft.Office.Interop.Excel.Application app,
Range target)
{
Range result = null;
foreach (Range r in target.Rows)
{
if (app.WorksheetFunction.CountA(r.Cells) >= 1)
continue;
result = result == null
? r.EntireRow
: app.Union(result, r.EntireRow);
}
return result;
}
private static Range GetEmptyColumns(
Microsoft.Office.Interop.Excel.Application app,
Range target)
{
Range result = null;
foreach (Range c in target.Columns)
{
if (app.WorksheetFunction.CountA(c.Cells) >= 1)
continue;
result = result == null
? c.EntireColumn
: app.Union(result, c.EntireColumn);
}
return result;
}
获取空范围 rows/columns 的两个函数可以重构为一个函数,如下所示:
private static Range GetEntireEmptyRowsOrColumns(
Microsoft.Office.Interop.Excel.Application app,
Range target,
Func<Range, Range> rowsOrColumns,
Func<Range, Range> entireRowOrColumn)
{
Range result = null;
foreach (Range c in rowsOrColumns(target))
{
if (app.WorksheetFunction.CountA(c.Cells) >= 1)
continue;
result = result == null
? entireRowOrColumn(c)
: app.Union(result, entireRowOrColumn(c));
}
return result;
}
然后直接调用它:
Range deleteColumns = GetEntireEmptyRowsOrColumns(exapp, target, (Func<Range, Range>)(r1 => r1.Columns), (Func<Range, Range>)(r2 => r2.EntireColumn));
Range deleteRows = GetEntireEmptyRowsOrColumns(exapp, target, (Func<Range, Range>)(r1 => r1.Rows), (Func<Range, Range>)(r2 => r2.EntireRow));
deleteColumns?.Delete();
deleteRows?.Delete();
注意:有关更多信息,请查看例如在 this SO question.
编辑
尝试简单地清除最后使用的单元格之后的所有单元格的内容。
public void Yahfoufi(string excelFile)
{
var exapp = new Microsoft.Office.Interop.Excel.Application {Visible = true};
var wrb = exapp.Workbooks.Open(excelFile);
var sh = wrb.Sheets["Sheet1"];
var lastRow = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByRows);
var lastCol = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByColumns);
// Clear the columns
sh.Range(sh.Cells(1, lastCol + 1), sh.Cells(1, Columns.Count)).EntireColumn.Clear();
// Clear the remaining cells
sh.Range(sh.Cells(lastRow + 1, 1), sh.Cells(Rows.Count, lastCol)).Clear();
}
更新 1
如果您的目标是使用 c# 导入 excel 数据,假设您已确定工作表中使用率最高的索引 (在您发布的图片中,它是 Col = 10 , Row = 16),您可以将最大使用索引转换为字母,这样它将 J16
和 select 仅使用范围 OLEDBCommand
SELECT * FROM [Sheet1$A1:J16]
否则,我认为找到更快的方法并不容易。
您可以参考这些文章将索引转换为字母并使用 OLEDB 连接到 excel:
- How to convert a column number (eg. 127) into an excel column (eg. AA)
初始答案
正如您所说,您是从以下问题开始的:
而您正在尝试 "get the last row containing data to remove all extra blanks (after this row , or column)"
所以假设你正在使用接受答案(由 @JohnG 提供),那么你可以添加一些代码行来获取最后使用的行和列
空行存储在整数列表中rowsToDelete
您可以使用以下代码获取索引小于最后一个空行的最后一个非空行
List<int> NonEmptyRows = Enumerable.Range(1, rowsToDelete.Max()).ToList().Except(rowsToDelete).ToList();
并且如果 NonEmptyRows.Max() < rowsToDelete.Max()
最后一个非空行是 NonEmptyRows.Max()
否则它是 worksheet.Rows.Count
并且在最后一个使用的行之后没有空行。
可以做同样的事情来获取最后一个非空列
代码在 DeleteCols
和 DeleteRows
函数中编辑:
private static void DeleteRows(List<int> rowsToDelete, Microsoft.Office.Interop.Excel.Worksheet worksheet)
{
// the rows are sorted high to low - so index's wont shift
List<int> NonEmptyRows = Enumerable.Range(1, rowsToDelete.Max()).ToList().Except(rowsToDelete).ToList();
if (NonEmptyRows.Max() < rowsToDelete.Max())
{
// there are empty rows after the last non empty row
Microsoft.Office.Interop.Excel.Range cell1 = worksheet.Cells[NonEmptyRows.Max() + 1,1];
Microsoft.Office.Interop.Excel.Range cell2 = worksheet.Cells[rowsToDelete.Max(), 1];
//Delete all empty rows after the last used row
worksheet.Range[cell1, cell2].EntireRow.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftUp);
} //else last non empty row = worksheet.Rows.Count
foreach (int rowIndex in rowsToDelete.Where(x => x < NonEmptyRows.Max()))
{
worksheet.Rows[rowIndex].Delete();
}
}
private static void DeleteCols(List<int> colsToDelete, Microsoft.Office.Interop.Excel.Worksheet worksheet)
{
// the cols are sorted high to low - so index's wont shift
//Get non Empty Cols
List<int> NonEmptyCols = Enumerable.Range(1, colsToDelete.Max()).ToList().Except(colsToDelete).ToList();
if (NonEmptyCols.Max() < colsToDelete.Max())
{
// there are empty rows after the last non empty row
Microsoft.Office.Interop.Excel.Range cell1 = worksheet.Cells[1,NonEmptyCols.Max() + 1];
Microsoft.Office.Interop.Excel.Range cell2 = worksheet.Cells[1,NonEmptyCols.Max()];
//Delete all empty rows after the last used row
worksheet.Range[cell1, cell2].EntireColumn.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftToLeft);
} //else last non empty column = worksheet.Columns.Count
foreach (int colIndex in colsToDelete.Where(x => x < NonEmptyCols.Max()))
{
worksheet.Columns[colIndex].Delete();
}
}
假设最后一个带有数据的角单元格是 J16 - 因此 K 列以上或第 17 行以下没有数据。你为什么要删除它们?场景是什么,你想达到什么目的?是否清除我们的格式?是否正在清除显示空字符串的公式?
无论如何,循环不是办法。
下面的代码显示了一种使用 Range 对象的 Clear() 方法来清除范围中的所有内容和公式以及格式的方法。或者,如果您确实要删除它们,可以使用 Delete() 方法一次删除整个矩形范围。会比循环快很多...
//code uses variables declared appropriately as Excel.Range & Excel.Worksheet Using Interop library
int x;
int y;
// get the row of the last value content row-wise
oRange = oSheet.Cells.Find(What: "*",
After: oSheet.get_Range("A1"),
LookIn: XlFindLookIn.xlValues,
LookAt: XlLookAt.xlPart,
SearchDirection: XlSearchDirection.xlPrevious,
SearchOrder: XlSearchOrder.xlByRows);
if (oRange == null)
{
return;
}
x = oRange.Row;
// get the column of the last value content column-wise
oRange = oSheet.Cells.Find(What: "*",
After: oSheet.get_Range("A1"),
LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlPart,
SearchDirection: XlSearchDirection.xlPrevious,
SearchOrder: XlSearchOrder.xlByColumns);
y = oRange.Column;
// now we have the corner (x, y), we can delete or clear all content to the right and below
// say J16 is the cell, so x = 16, and j=10
Excel.Range clearRange;
//set clearRange to ("K1:XFD1048576")
clearRange = oSheet.Range[oSheet.Cells[1, y + 1], oSheet.Cells[oSheet.Rows.Count, oSheet.Columns.Count]];
clearRange.Clear(); //clears all content, formulas and formatting
//clearRange.Delete(); if you REALLY want to hard delete the rows
//set clearRange to ("A17:J1048576")
clearRange = oSheet.Range[oSheet.Cells[x + 1, 1], oSheet.Cells[oSheet.Rows.Count, y]];
clearRange.Clear(); //clears all content, formulas and formatting
//clearRange.Delete(); if you REALLY want to hard delete the columns
看来你的问题已经被微软解决了。看看 Range.CurrentRegion Property,其中 returns 由空白行和空白列的任意组合界定的范围。有一个不便之处:此 属性 不能用于受保护的工作表。
详情请见:How to Find Current Region, Used Range, Last Row and Last Column in Excel with VBA Macro
一些 SO 成员提到了 UsedRange property,这可能也很有用,但与 CurrentRegion
的不同之处在于 UsedRange
returns 范围包括任何单元格曾经被使用过。
所以,如果你想得到一个LAST(row)
和LAST(column)
被数据占用,你必须使用End property和XlDirection
:xlToLeft
and/or xlUp
。
注意#1:
如果您的数据是表格格式,您可以简单地找到最后一个单元格,使用:
lastCell = yourWorkseet.UsedRange.End(xlUp)
firstEmtyRow = lastCell.Offset(RowOffset:=1).EntireRow
注意#2:
如果您的数据不是表格格式,您需要遍历行和列的集合以找到最后一个非空白单元格。
祝你好运!
我想你可以尝试使用 Range。
Application excel = new Application();
Workbook workBook= excel.Workbooks.Open("file.xlsx")
Worksheet excelSheet = workBook.ActiveSheet;
Range excelRange = excelSheet.UsedRange.Columns[1, Missing.Value] as Range;
var lastNonEmptyRow = excelRange.Cells.Count;
上面的代码对我有用。
我正在尝试使用 Interop Library 从 excel 文件中删除所有多余的空白行和列。
我关注了这个问题
但我有 excel 个文件,其中包含一小部分数据,但有很多空行和空列 (从最后一个非空行(或列)到工作表)
我尝试循环遍历行和列,但循环需要几个小时。
我正在尝试获取最后一个非空行和列索引,以便我可以在一行中删除整个空范围
XlWks.Range("...").EntireRow.Delete(xlShiftUp)
注意:我正在尝试获取包含数据的最后一行以删除所有多余的空白(在此行或列之后)
有什么建议吗?
注意:代码必须兼容SSIS脚本任务环境
您应该能够找到与此类似的最后一个非空行和列:
with m_XlWrkSheet
lastRow = .UsedRange.Rows.Count
lastCol = .UsedRange.Columns.Count
end with
那是 VB.NET,但它应该或多或少起作用。那将 return 第 16 行和第 10 列(根据您上面的图片)。然后你可以用它来找到你想在一行中全部删除的范围。
我正在使用 ClosedXml,它具有有用的 'LastUsedRow' 和 'LastUsedColumn' 方法。
var wb = new XLWorkbook(@"<path>\test.xlsx", XLEventTracking.Disabled);
var sheet = wb.Worksheet("Sheet1");
for (int i = sheet.LastRowUsed().RowNumber() - 1; i >= 1; i--)
{
var row = sheet.Row(i);
if (row.IsEmpty())
{
row.Delete();
}
}
wb.Save();
这个简单的循环在 38 秒内删除了 10000 行中的 5000 行。不快,但比 'hours' 好很多。这取决于您处理的 rows/columns 数量,当然您没有说。 但是,在对 50000 行中的 25000 行进行进一步测试后,删除循环中的空行确实需要大约 30 分钟。显然删除行不是一个有效的过程。
更好的解决方案是创建一个新的 sheet,然后复制您要保留的行。
第 1 步 - 创建 sheet 50000 行和 20 列,每隔一行和一列为空。
var wb = new XLWorkbook(@"C:\Users\passp\Documents\test.xlsx");
var sheet = wb.Worksheet("Sheet1");
sheet.Clear();
for (int i = 1; i < 50000; i+=2)
{
var row = sheet.Row(i);
for (int j = 1; j < 20; j += 2)
{
row.Cell(j).Value = i * j;
}
}
第 2 步 - 将包含数据的行复制到新的 sheet。这需要 10 秒。
var wb = new XLWorkbook(@"C:\Users\passp\Documents\test.xlsx", XLEventTracking.Disabled);
var sheet = wb.Worksheet("Sheet1");
var sheet2 = wb.Worksheet("Sheet2");
sheet2.Clear();
sheet.RowsUsed()
.Where(r => !r.IsEmpty())
.Select((r, index) => new { Row = r, Index = index + 1} )
.ForEach(r =>
{
var newRow = sheet2.Row(r.Index);
r.Row.CopyTo(newRow);
}
);
wb.Save();
第 3 步 - 这将对列执行相同的操作。
几年前我创建了一个 MSDN 代码示例,允许开发人员从作品中获取最后使用的行和列sheet。我对其进行了修改,将所有需要的代码放入一个带有 windows 表单前端的 class 库中以演示该操作。
底层代码使用Microsoft.Office.Interop.Excel。
Microsoft 一个驱动器上的位置 https://1drv.ms/u/s!AtGAgKKpqdWjiEGdBzWDCSCZAMaM
在这里,我获取 Excel 文件中的第一个 sheet,获取最后使用的行和列,并作为有效的单元格地址显示。
Private Sub cmdAddress1_Click(sender As Object, e As EventArgs) Handles cmdAddress1.Click
Dim ops As New GetExcelColumnLastRowInformation
Dim info = New UsedInformation
ExcelInformationData = info.UsedInformation(FileName, ops.GetSheets(FileName))
Dim SheetName As String = ExcelInformationData.FirstOrDefault.SheetName
Dim cellAddress = (
From item In ExcelInformationData
Where item.SheetName = ExcelInformationData.FirstOrDefault.SheetName
Select item.LastCell).FirstOrDefault
MessageBox.Show($"{SheetName} - {cellAddress}")
End Sub
在演示项目中,我还获取了 excel 文件的所有 sheet,并将它们显示在列表框中。 Select 列表框中的 sheet 名称,并在有效的单元格地址中获取 sheet 的最后一行和最后一列。
Private Sub cmdAddress_Click(sender As Object, e As EventArgs) Handles cmdAddress.Click
Dim cellAddress =
(
From item In ExcelInformationData
Where item.SheetName = ListBox1.Text
Select item.LastCell).FirstOrDefault
If cellAddress IsNot Nothing Then
MessageBox.Show($"{ListBox1.Text} {cellAddress}")
End If
End Sub
从上面的 link 打开解决方案时,乍一看,您会注意到有很多代码。代码是最优的,会立即释放所有对象。
- 要获取最后一个非空 column/row 索引,可以使用 Excel 函数
Find
。参见GetLastIndexOfNonEmptyCell
。 - 然后Excel工作表函数
CountA
判断单元格是否为空和union整个 rows/columns 到一个 rows/columns 范围。 - 最后一次删除这个范围。
public void Yahfoufi(string excelFile)
{
var exapp = new Microsoft.Office.Interop.Excel.Application {Visible = true};
var wrb = exapp.Workbooks.Open(excelFile);
var sh = wrb.Sheets["Sheet1"];
var lastRow = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByRows);
var lastCol = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByColumns);
var target = sh.Range[sh.Range["A1"], sh.Cells[lastRow, lastCol]];
Range deleteRows = GetEmptyRows(exapp, target);
Range deleteColumns = GetEmptyColumns(exapp, target);
deleteColumns?.Delete();
deleteRows?.Delete();
}
private static int GetLastIndexOfNonEmptyCell(
Microsoft.Office.Interop.Excel.Application app,
Worksheet sheet,
XlSearchOrder searchOrder)
{
Range rng = sheet.Cells.Find(
What: "*",
After: sheet.Range["A1"],
LookIn: XlFindLookIn.xlFormulas,
LookAt: XlLookAt.xlPart,
SearchOrder: searchOrder,
SearchDirection: XlSearchDirection.xlPrevious,
MatchCase: false);
if (rng == null)
return 1;
return searchOrder == XlSearchOrder.xlByRows
? rng.Row
: rng.Column;
}
private static Range GetEmptyRows(
Microsoft.Office.Interop.Excel.Application app,
Range target)
{
Range result = null;
foreach (Range r in target.Rows)
{
if (app.WorksheetFunction.CountA(r.Cells) >= 1)
continue;
result = result == null
? r.EntireRow
: app.Union(result, r.EntireRow);
}
return result;
}
private static Range GetEmptyColumns(
Microsoft.Office.Interop.Excel.Application app,
Range target)
{
Range result = null;
foreach (Range c in target.Columns)
{
if (app.WorksheetFunction.CountA(c.Cells) >= 1)
continue;
result = result == null
? c.EntireColumn
: app.Union(result, c.EntireColumn);
}
return result;
}
获取空范围 rows/columns 的两个函数可以重构为一个函数,如下所示:
private static Range GetEntireEmptyRowsOrColumns(
Microsoft.Office.Interop.Excel.Application app,
Range target,
Func<Range, Range> rowsOrColumns,
Func<Range, Range> entireRowOrColumn)
{
Range result = null;
foreach (Range c in rowsOrColumns(target))
{
if (app.WorksheetFunction.CountA(c.Cells) >= 1)
continue;
result = result == null
? entireRowOrColumn(c)
: app.Union(result, entireRowOrColumn(c));
}
return result;
}
然后直接调用它:
Range deleteColumns = GetEntireEmptyRowsOrColumns(exapp, target, (Func<Range, Range>)(r1 => r1.Columns), (Func<Range, Range>)(r2 => r2.EntireColumn));
Range deleteRows = GetEntireEmptyRowsOrColumns(exapp, target, (Func<Range, Range>)(r1 => r1.Rows), (Func<Range, Range>)(r2 => r2.EntireRow));
deleteColumns?.Delete();
deleteRows?.Delete();
注意:有关更多信息,请查看例如在 this SO question.
编辑
尝试简单地清除最后使用的单元格之后的所有单元格的内容。
public void Yahfoufi(string excelFile)
{
var exapp = new Microsoft.Office.Interop.Excel.Application {Visible = true};
var wrb = exapp.Workbooks.Open(excelFile);
var sh = wrb.Sheets["Sheet1"];
var lastRow = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByRows);
var lastCol = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByColumns);
// Clear the columns
sh.Range(sh.Cells(1, lastCol + 1), sh.Cells(1, Columns.Count)).EntireColumn.Clear();
// Clear the remaining cells
sh.Range(sh.Cells(lastRow + 1, 1), sh.Cells(Rows.Count, lastCol)).Clear();
}
更新 1
如果您的目标是使用 c# 导入 excel 数据,假设您已确定工作表中使用率最高的索引 (在您发布的图片中,它是 Col = 10 , Row = 16),您可以将最大使用索引转换为字母,这样它将 J16
和 select 仅使用范围 OLEDBCommand
SELECT * FROM [Sheet1$A1:J16]
否则,我认为找到更快的方法并不容易。
您可以参考这些文章将索引转换为字母并使用 OLEDB 连接到 excel:
- How to convert a column number (eg. 127) into an excel column (eg. AA)
初始答案
正如您所说,您是从以下问题开始的:
而您正在尝试 "get the last row containing data to remove all extra blanks (after this row , or column)"
所以假设你正在使用接受答案(由 @JohnG 提供),那么你可以添加一些代码行来获取最后使用的行和列
空行存储在整数列表中rowsToDelete
您可以使用以下代码获取索引小于最后一个空行的最后一个非空行
List<int> NonEmptyRows = Enumerable.Range(1, rowsToDelete.Max()).ToList().Except(rowsToDelete).ToList();
并且如果 NonEmptyRows.Max() < rowsToDelete.Max()
最后一个非空行是 NonEmptyRows.Max()
否则它是 worksheet.Rows.Count
并且在最后一个使用的行之后没有空行。
可以做同样的事情来获取最后一个非空列
代码在 DeleteCols
和 DeleteRows
函数中编辑:
private static void DeleteRows(List<int> rowsToDelete, Microsoft.Office.Interop.Excel.Worksheet worksheet)
{
// the rows are sorted high to low - so index's wont shift
List<int> NonEmptyRows = Enumerable.Range(1, rowsToDelete.Max()).ToList().Except(rowsToDelete).ToList();
if (NonEmptyRows.Max() < rowsToDelete.Max())
{
// there are empty rows after the last non empty row
Microsoft.Office.Interop.Excel.Range cell1 = worksheet.Cells[NonEmptyRows.Max() + 1,1];
Microsoft.Office.Interop.Excel.Range cell2 = worksheet.Cells[rowsToDelete.Max(), 1];
//Delete all empty rows after the last used row
worksheet.Range[cell1, cell2].EntireRow.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftUp);
} //else last non empty row = worksheet.Rows.Count
foreach (int rowIndex in rowsToDelete.Where(x => x < NonEmptyRows.Max()))
{
worksheet.Rows[rowIndex].Delete();
}
}
private static void DeleteCols(List<int> colsToDelete, Microsoft.Office.Interop.Excel.Worksheet worksheet)
{
// the cols are sorted high to low - so index's wont shift
//Get non Empty Cols
List<int> NonEmptyCols = Enumerable.Range(1, colsToDelete.Max()).ToList().Except(colsToDelete).ToList();
if (NonEmptyCols.Max() < colsToDelete.Max())
{
// there are empty rows after the last non empty row
Microsoft.Office.Interop.Excel.Range cell1 = worksheet.Cells[1,NonEmptyCols.Max() + 1];
Microsoft.Office.Interop.Excel.Range cell2 = worksheet.Cells[1,NonEmptyCols.Max()];
//Delete all empty rows after the last used row
worksheet.Range[cell1, cell2].EntireColumn.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftToLeft);
} //else last non empty column = worksheet.Columns.Count
foreach (int colIndex in colsToDelete.Where(x => x < NonEmptyCols.Max()))
{
worksheet.Columns[colIndex].Delete();
}
}
假设最后一个带有数据的角单元格是 J16 - 因此 K 列以上或第 17 行以下没有数据。你为什么要删除它们?场景是什么,你想达到什么目的?是否清除我们的格式?是否正在清除显示空字符串的公式?
无论如何,循环不是办法。
下面的代码显示了一种使用 Range 对象的 Clear() 方法来清除范围中的所有内容和公式以及格式的方法。或者,如果您确实要删除它们,可以使用 Delete() 方法一次删除整个矩形范围。会比循环快很多...
//code uses variables declared appropriately as Excel.Range & Excel.Worksheet Using Interop library
int x;
int y;
// get the row of the last value content row-wise
oRange = oSheet.Cells.Find(What: "*",
After: oSheet.get_Range("A1"),
LookIn: XlFindLookIn.xlValues,
LookAt: XlLookAt.xlPart,
SearchDirection: XlSearchDirection.xlPrevious,
SearchOrder: XlSearchOrder.xlByRows);
if (oRange == null)
{
return;
}
x = oRange.Row;
// get the column of the last value content column-wise
oRange = oSheet.Cells.Find(What: "*",
After: oSheet.get_Range("A1"),
LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlPart,
SearchDirection: XlSearchDirection.xlPrevious,
SearchOrder: XlSearchOrder.xlByColumns);
y = oRange.Column;
// now we have the corner (x, y), we can delete or clear all content to the right and below
// say J16 is the cell, so x = 16, and j=10
Excel.Range clearRange;
//set clearRange to ("K1:XFD1048576")
clearRange = oSheet.Range[oSheet.Cells[1, y + 1], oSheet.Cells[oSheet.Rows.Count, oSheet.Columns.Count]];
clearRange.Clear(); //clears all content, formulas and formatting
//clearRange.Delete(); if you REALLY want to hard delete the rows
//set clearRange to ("A17:J1048576")
clearRange = oSheet.Range[oSheet.Cells[x + 1, 1], oSheet.Cells[oSheet.Rows.Count, y]];
clearRange.Clear(); //clears all content, formulas and formatting
//clearRange.Delete(); if you REALLY want to hard delete the columns
看来你的问题已经被微软解决了。看看 Range.CurrentRegion Property,其中 returns 由空白行和空白列的任意组合界定的范围。有一个不便之处:此 属性 不能用于受保护的工作表。
详情请见:How to Find Current Region, Used Range, Last Row and Last Column in Excel with VBA Macro
一些 SO 成员提到了 UsedRange property,这可能也很有用,但与 CurrentRegion
的不同之处在于 UsedRange
returns 范围包括任何单元格曾经被使用过。
所以,如果你想得到一个LAST(row)
和LAST(column)
被数据占用,你必须使用End property和XlDirection
:xlToLeft
and/or xlUp
。
注意#1:
如果您的数据是表格格式,您可以简单地找到最后一个单元格,使用:
lastCell = yourWorkseet.UsedRange.End(xlUp)
firstEmtyRow = lastCell.Offset(RowOffset:=1).EntireRow
注意#2:
如果您的数据不是表格格式,您需要遍历行和列的集合以找到最后一个非空白单元格。
祝你好运!
我想你可以尝试使用 Range。
Application excel = new Application();
Workbook workBook= excel.Workbooks.Open("file.xlsx")
Worksheet excelSheet = workBook.ActiveSheet;
Range excelRange = excelSheet.UsedRange.Columns[1, Missing.Value] as Range;
var lastNonEmptyRow = excelRange.Cells.Count;
上面的代码对我有用。