Удалить пустые строки с помощью Excel Interop
У меня есть пользовательские файлы excel, которые необходимо преобразовать в PDF. Используя excel interop, я могу сделать это с помощью .ExportAsFixedFormat()
. Моя проблема возникает, когда книга содержит миллионы строк. Это превращается в файл с 50k + страницами. Это было бы хорошо, если бы книга имела контент во всех этих строках. Каждый раз, когда появляется один из этих файлов, возможно, есть 50 строк, у которых есть контент, а остальные пустые. Как я могу удалить удаленные строки, чтобы я мог экспортировать их в файл PDF с хорошим размером?
-
Я попытался начать с конечной строки и, один за другим, используя CountA
, чтобы проверить, имеет ли строка содержимое, и если это так, удалите его. Это происходит не только навсегда, но, похоже, не работает примерно через 100k строк со следующей ошибкой:
Невозможно оценить выражение, потому что код оптимизирован, или собственный кадр находится поверх стека вызовов.
-
Я пробовал использовать SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues)
, но это включает строку, если какая-либо ячейка имеет форматирование (например, цвет bg).
-
Я пробовал использовать Worksheet.UsedRange
, а затем удалять все после этого, но UsedRange
имеет ту же проблему, что и вторая.
<ч/" > Это код, который я пробовал:
for (int i = 0; i < worksheets.Count; i++)
{
sheet = worksheets[i + 1];
rows = sheet.Rows;
currentRowIndex = rows.Count;
bool contentFound = false;
while (!contentFound && currentRowIndex > 0)
{
currentRow = rows[currentRowIndex];
if (Application.WorksheetFunction.CountA(currentRow) == 0)
{
currentRow.Delete();
}
else
{
contentFound = true;
}
Marshal.FinalReleaseComObject(currentRow);
currentRowIndex--;
}
Marshal.FinalReleaseComObject(rows);
Marshal.FinalReleaseComObject(sheet);
}
for (int i = 0; i < worksheets.Count; i++)
{
sheet = worksheets[i + 1];
rows = sheet.Rows;
lastCell = rows.SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues);
int startRow = lastCell.Row;
Range range = sheet.get_Range(lastCell.get_Address(RowAbsolute: startRow));
range.Delete();
Marshal.FinalReleaseComObject(range);
Marshal.FinalReleaseComObject(lastCell);
Marshal.FinalReleaseComObject(rows);
Marshal.FinalReleaseComObject(sheet);
}
У меня проблема с моим кодом, это проблема взаимодействия или, может быть, это просто ограничение на то, что может сделать Excel? Есть ли лучший способ сделать то, что я пытаюсь?
Ответы
Ответ 1
Вы пробовали Sheet1.Range("A1").CurrentRegion.ExportAsFixedFormat()
, где Sheet1 является допустимым именем листа, а "A1" - это ячейка, которую вы можете протестировать, чтобы убедиться, что она находится в диапазоне, который вы хотите экспортировать?
Вопрос остается, почему Excel считает, что в этих "пустых" ячейках есть данные? Форматирование? Предварительно существующая область печати, которую необходимо очистить? Я знаю, что раньше сталкивался с подобными ситуациями, это единственные возможности, которые приходят на ум в этот момент.
Ответ 2
Попробуйте выполнить следующие действия -
- скопируйте
Worksheet.UsedRange
на отдельный лист (лист2).
- используйте специальную пасту, чтобы сохранить форматирование
- попробуйте разбор листа2 для неиспользуемых строк
Если это не поможет повторить шаг 2 с очищаемой информацией форматирования и затем разобрать лист2. вы всегда можете скопировать информацию о формате позже (если они достаточно просты)
Ответ 3
Если вы можете сначала загрузить файл Excel в DataSet через OleDBAdapter, относительно легко удалить пустые строки при импорте...
Попробуйте OleDBAdapter Excel QA Я опубликовал через переполнение стека.
Затем экспортируйте DataSet в новый файл Excel и преобразуйте этот файл в PDF. Это может быть большой "IF", хотя, конечно, в зависимости от макета excel (или отсутствия там).
Ответ 4
Мне пришлось решить эту проблему сегодня для того, что может быть подмножеством ваших возможных случаев.
Если ваша таблица соответствует следующим условиям:
- Все столбцы с данными имеют текст заголовка в строке 1.
- Все строки с данными находятся в последовательности до первой строки BLANK.
Тогда следующий код может помочь:
private static string[,] LoadCellData(Excel.Application excel, dynamic sheet)
{
int countCols = CountColsToFirstBlank(excel, sheet);
int countRows = CountRowsToFirstBlank(excel, sheet);
cellData = new string[countCols, countRows];
string datum;
for (int i = 0; i < countCols; i++)
{
for (int j = 0; j < countRows; j++)
{
try
{
if (null != sheet.Cells[i + 1, j + 1].Value)
{
datum = excel.Cells[i + 1, j + 1].Value.ToString();
cellData[i, j] = datum;
}
}
catch (Exception ex)
{
lastException = ex;
//Console.WriteLine(String.Format("LoadCellData [{1}, {2}] reported an error: [{0}]", ex.Message, i, j));
}
}
}
return cellData;
}
private static int CountRowsToFirstBlank(Excel.Application excel, dynamic sheet)
{
int count = 0;
for (int j = 0; j < sheet.UsedRange.Rows.Count; j++)
{
if (IsBlankRow(excel, sheet, j + 1))
break;
count++;
}
return count;
}
private static int CountColsToFirstBlank(Excel.Application excel, dynamic sheet)
{
int count = 0;
for (int i = 0; i < sheet.UsedRange.Columns.Count; i++)
{
if (IsBlankCol(excel, sheet, i + 1))
break;
count++;
}
return count;
}
private static bool IsBlankCol(Excel.Application excel, dynamic sheet, int col)
{
for (int i = 0; i < sheet.UsedRange.Rows.Count; i++)
{
if (null != sheet.Cells[i + 1, col].Value)
{
return false;
}
}
return true;
}
private static bool IsBlankRow(Excel.Application excel, dynamic sheet, int row)
{
for (int i = 0; i < sheet.UsedRange.Columns.Count; i++)
{
if (null != sheet.Cells[i + 1, row].Value)
{
return false;
}
}
return true;
}
Ответ 5
Я бы предложил вам получить количество строк, которые содержат некоторые значения, используя CountA (как вы пробовали в пункте 1). Затем скопируйте эти строки в новый лист и экспортируйте их оттуда. Будет легче скопировать несколько строк на новый лист и работать над ним, а не пытаться удалить огромное количество строк из исходного листа.
Для создания новых листов и копирования строк вы можете использовать следующий код:
excel.Worksheet tempSheet = workbook.Worksheets.Add();
tempSheet.Name = sheetName;
workbook.Save();
//создаем новый метод для копирования новых строк
//как rowindex вы можете передать общее количество строк, которые вы обнаружили с помощью CountA
public void CopyRows(excel.Workbook workbook, string sourceSheetName, string DestSheetName, int rowIndex)
{
excel.Worksheet sourceSheet = (excel.Worksheet)workbook.Sheets[sourceSheetName];
excel.Range source = (excel.Range)sourceSheet.Range["A" + rowIndex.ToString(), Type.Missing].EntireRow;
excel.Worksheet destSheet = (excel.Worksheet)workbook.Sheets[DestSheetName];
excel.Range dest = (excel.Range)destSheet.Range["A" + rowIndex.ToString(), Type.Missing].EntireRow;
source.Copy(dest);
excel.Range newRow = (excel.Range)destSheet.Rows[rowIndex+1];
newRow.Insert();
workbook.Save();
}
Ответ 6
Пожалуйста, попробуйте следующий код:
for (int i = 0; i < worksheets.Count; i++)
{
sheet = worksheets[i + 1];
sheet.Columns("A:A").SpecialCells(XlCellType.xlCellTypeBlanks).EntireRow.Delete
sheet.Rows("1:1").SpecialCells(XlCellType.xlCellTypeBlanks).EntireColumn.Delete
Marshal.FinalReleaseComObject(sheet);
}