Удалить пустые строки с помощью Excel Interop - PullRequest
14 голосов
/ 21 марта 2011

У меня есть файлы Excel, которые необходимо преобразовать в PDF. Используя Excel Interop, я могу сделать это хорошо с .ExportAsFixedFormat(). Моя проблема возникает, когда в книге миллионы строк. Это превращается в файл, который имеет 50 000 страниц. Это было бы хорошо, если бы во всех этих строках содержалось содержание книги. Каждый раз, когда появляется один из этих файлов, возможно, есть 50 строк с содержанием, а остальные пустые. Как я могу удалить пустые строки, чтобы экспортировать их в PDF приличного размера?

  1. Я попытался начать с конца строки и, один за другим, использовать CountA, чтобы проверить, есть ли в строке содержимое, и если оно есть, удалить его. Мало того, что это занимает вечность, но, похоже, происходит сбой после примерно 100 тысяч строк со следующей ошибкой:

    Невозможно оценить выражение, потому что код оптимизирован или собственный фрейм находится над стеком вызовов.

  2. Я пытался использовать SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues), но это включает строку, если какая-либо ячейка имеет форматирование (например, цвет bg).

  3. Я пытался использовать 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? Есть ли лучший способ сделать то, что я пытаюсь?

Ответы [ 7 ]

1 голос
/ 14 декабря 2017

Я бы предложил вам подсчитать количество строк, которые содержат некоторые значения, используя CountA (как вы пытались в пункте 1).Затем скопируйте эти строки в новый лист и экспортируйте его оттуда.Будет проще скопировать несколько строк на новый лист и работать с ним, а не пытаться удалять огромное количество строк из исходного листа.

Для создания нового листа и копирования строк вы можете использовать следующий код:

        excel.Worksheet tempSheet = workbook.Worksheets.Add();
        tempSheet.Name = sheetName;
        workbook.Save();

// создаем новый метод для копирования новых строк

// в качестве индекса строки вы можете передать общее количество найденных вами строк, используя 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();
        }
0 голосов
/ 19 марта 2018

Можете ли вы попробовать с приведенным ниже кодом:

for (int rowIndex = workSheet.Dimension.Start.Row; rowIndex <= workSheet.Dimension.End.Row; rowIndex++)
                    {
                        //Assume the first row is the header. Then use the column match ups by name to determine the index.
                        //This will allow you to have the order of the header.Keys change without any affect.
                        var row = workSheet.Cells[string.Format("{0}:{0}", rowIndex)];
                        // check if the row and column cells are empty
                        bool allEmpty = row.All(c => string.IsNullOrWhiteSpace(c.Text));
                        if (allEmpty)
                            continue; // skip this row
                        else{
                               //here read header
                               if()
                                 {
                                  //some code
                                 }
                               else
                                  {
                                   //some code to read body
                                  }
                            }
                    }

Надеюсь, эта помощь, иначе дайте мне знать, если вам нужно описание кода.

Обновлено:

  • код ниже используется для проверки количества строк в таблице. цикл for будет проходить до конца строки листа.

для (int rowIndex = workSheet.Dimension.Start.Row; rowIndex <= workSheet.Dimension.End.Row; rowIndex ++) </p>

  • здесь мы проверяем, являются ли ячейки строк и столбцов пустыми, используя linq:

bool allEmpty = row.All (c => string.IsNullOrWhiteSpace (c.Text));
if (allEmpty)
Продолжить; // если true, пропустите эту строку
еще // читаем заголовки (при условии, что они представлены в рабочем листе)
// еще читать данные по строкам а затем выполните необходимые шаги.

в надежде, что это прояснится сейчас.

0 голосов
/ 22 ноября 2016

Пожалуйста, попробуйте следующий код:

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);
}
0 голосов
/ 21 мая 2012

Я должен был решить эту проблему сегодня для того, что может быть подмножеством ваших возможных случаев.

Если ваша таблица соответствует следующим условиям:

  1. Все столбцы с данными имеют текст заголовка в строке 1.
  2. Все строки с данными находятся в последовательности до первой пустой строки.

Тогда может помочь следующий код:

    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;
    }
0 голосов
/ 05 мая 2011

Если вы можете сначала загрузить файл Excel в DataSet с помощью OleDBAdapter, относительно легко удалить пустые строки при импорте ... Попробуйте это OleDBAdapter Excel QA Я отправил через переполнение стека.

Затем экспортируйте DataSet в новый файл Excel и преобразуйте этот файл в PDF.Это может быть большим «IF», ​​хотя, конечно, в зависимости от макета Excel (или его отсутствия).

0 голосов
/ 22 марта 2011

Попробуйте выполнить следующие действия -

  1. скопируйте Worksheet.UsedRange на отдельный лист (лист2).
  2. используйте специальную вставку, чтобы сохранить форматирование
  3. попробуйте выполнить синтаксический анализ листа2 для неиспользуемых строк

Если это не помогает, попробуйте повторить шаг 2 с очищенной информацией о форматировании, а затем выполните синтаксический анализ листа2.Вы всегда можете скопировать информацию о формате позже (если она достаточно проста)

0 голосов
/ 22 марта 2011

Вы пробовали Sheet1.Range("A1").CurrentRegion.ExportAsFixedFormat(), где Sheet1 - это действительное имя листа, а "A1" - это ячейка, которую вы можете проверить, чтобы убедиться, что она находится в диапазоне, который вы хотите экспортировать?

Остается вопрос, почему Excel считает, что в этих «пустых» ячейках есть данные? Форматирование? Существующая область печати, которая должна быть очищена? Я знаю, что сталкивался с подобными ситуациями раньше, это единственные возможности, которые приходят на ум в данный момент.

...