Удалить пустые строки с помощью 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);
}