Вопрос по openxml, openxml-sdk, c# – Использование OpenXmlReader

13

Я ненавижу прибегать к StackOverflow для чего-то столь (на первый взгляд) элементарного, но я боролся с Microsoft последние несколько часов и, похоже, зашел в тупик. Я пытаюсь читать (большие) таблицы Excel 2007+, и Google любезно сообщил мне, что использование OpenXml SDK является довольно популярным выбором. Поэтому я попробовал эту штуку, прочитал несколько учебных пособий, проверил собственные страницы библиотеки Microsoft и получил от них очень мало.

Я использую небольшую тестовую электронную таблицу с одним столбцом чисел и одной строкой - масштабное тестирование будет позже. Я пробовал несколько реализаций, похожих на ту, которую я собираюсь опубликовать, и ни одна из них не считала данные. Приведенный ниже код был в основном взят из другого потока StackOverflow, где он, похоже, работал - не для меня. Я рассчитывал, что вы, ребята, проверите / debug / help с этой версией, потому что она, вероятно, будет менее сломанной, чем все, что я написал сегодня.

<code>static void ReadExcelFileSAX(string fileName)
    {
        using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, true))
        {
            WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
            WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();

            OpenXmlPartReader reader = new OpenXmlPartReader(worksheetPart);
            string text;
            string rowNum;
            while (reader.Read())
            {
                if (reader.ElementType == typeof(Row))
                {
                    do
                    {
                        if (reader.HasAttributes)
                        {
                            rowNum = reader.Attributes.First(a => a.LocalName == "r").Value;
                            Console.Write("rowNum: " + rowNum); //we never even get here, I tested it with a breakpoint
                        }

                    } while (reader.ReadNextSibling()); // Skip to the next row
                    Console.ReadKey();
                    break; // We just looped through all the rows so no need to continue reading the worksheet
                }
                if (reader.ElementType == typeof(Cell))
                {

                }

                if (reader.ElementType != typeof(Worksheet)) // Dont' want to skip the contents of the worksheet
                    reader.Skip(); // Skip contents of any node before finding the first row.
            }
            reader.Close();
            Console.WriteLine();
            Console.ReadKey();
        }
    }
</code>

И, кроме того, есть ли хорошие альтернативы использованию OpenXml SDK, которые я как-то пропустил?

Попробуйте использовать инструмент повышения производительности Open XML SDK 2.0, доступный здесь: Microsoft.com / EN-US / скачать / details.aspx? ID = 5124. Это позволяет открыть любой XLSX и посмотреть его структуру или посмотреть код C #, который может воссоздать файл. Таким образом, вы можете увидеть, где находятся значения, которые вы хотите получить в файле, с которым работаете. Lukasz M

Ваш Ответ

2   ответа
0

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

public static DataTable ReadIntoDatatableFromExcel(string newFilePath)
        {
            /*Creating a table with 20 columns*/
            var dt = CreateProviderRvenueSharingTable();

            try
            {
                /*using stream so that if excel file is in another process then it can read without error*/
                using (Stream stream = new FileStream(newFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(stream, false))
                    {
                        var workbookPart = spreadsheetDocument.WorkbookPart;
                        var workbook = workbookPart.Workbook;

                        /*get only unhide tabs*/
                        var sheets = workbook.Descendants<Sheet>().Where(e => e.State == null);

                        foreach (var sheet in sheets)
                        {
                            var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);

                            /*Remove empty sheets*/
                            List<Row> rows = worksheetPart.Worksheet.Elements<SheetData>().First().Elements<Row>()
                                .Where(r => r.InnerText != string.Empty).ToList();

                            if (rows.Count > 1)
                            {
                                OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);

                                int i = 0;
                                int BTR = 0;/*Break the reader while empty rows are found*/

                                while (reader.Read())
                                {
                                    if (reader.ElementType == typeof(Row))
                                    {
                                        /*ignoring first row with headers and check if data is there after header*/
                                        if (i < 2)
                                        {
                                            i++;
                                            continue;
                                        }

                                        reader.ReadFirstChild();

                                        DataRow row = dt.NewRow();

                                        int CN = 0;

                                        if (reader.ElementType == typeof(Cell))
                                        {
                                            do
                                            {
                                                Cell c = (Cell)reader.LoadCurrentElement();

                                                /*reader skipping blank cells so data is getting worng in datatable's rows according to header*/
                                                if (CN != 0)
                                                {
                                                    int cellColumnIndex =
                                                        ExcelHelper.GetColumnIndexFromName(
                                                            ExcelHelper.GetColumnName(c.CellReference));

                                                    if (cellColumnIndex < 20 && CN < cellColumnIndex - 1)
                                                    {
                                                        do
                                                        {
                                                            row[CN] = string.Empty;
                                                            CN++;
                                                        } while (CN < cellColumnIndex - 1);
                                                    }
                                                }

                                                /*stopping execution if first cell does not have any value which means empty row*/
                                                if (CN == 0 && c.DataType == null && c.CellValue == null)
                                                {
                                                    BTR++;
                                                    break;
                                                }

                                                string cellValue = GetCellValue(c, workbookPart);
                                                row[CN] = cellValue;
                                                CN++;

                                                /*if any text exists after T column (index 20) then skip the reader*/
                                                if (CN == 20)
                                                {
                                                    break;
                                                }
                                            } while (reader.ReadNextSibling());
                                        }

                                        /*reader skipping blank cells so fill the array upto 19 index*/
                                        while (CN != 0 && CN < 20)
                                        {
                                            row[CN] = string.Empty;
                                            CN++;
                                        }

                                        if (CN == 20)
                                        {
                                            dt.Rows.Add(row);
                                        }
                                    }
                                    /*escaping empty rows below data filled rows after checking 5 times */
                                    if (BTR > 5)
                                        break;
                                }
                                reader.Close();
                            }                            
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return dt;
        }

  private static string GetCellValue(Cell c, WorkbookPart workbookPart)
        {
            string cellValue = string.Empty;
            if (c.DataType != null && c.DataType == CellValues.SharedString)
            {
                SharedStringItem ssi =
                    workbookPart.SharedStringTablePart.SharedStringTable
                        .Elements<SharedStringItem>()
                        .ElementAt(int.Parse(c.CellValue.InnerText));
                if (ssi.Text != null)
                {
                    cellValue = ssi.Text.Text;
                }
            }
            else
            {
                if (c.CellValue != null)
                {
                    cellValue = c.CellValue.InnerText;
                }
            }
            return cellValue;
        }

public static int GetColumnIndexFromName(string columnNameOrCellReference)
        {
            int columnIndex = 0;
            int factor = 1;
            for (int pos = columnNameOrCellReference.Length - 1; pos >= 0; pos--)   // R to L
            {
                if (Char.IsLetter(columnNameOrCellReference[pos]))  // for letters (columnName)
                {
                    columnIndex += factor * ((columnNameOrCellReference[pos] - 'A') + 1);
                    factor *= 26;
                }
            }
            return columnIndex;
        }

        public static string GetColumnName(string cellReference)
        {
            /* Advance from L to R until a number, then return 0 through previous position*/
            for (int lastCharPos = 0; lastCharPos <= 3; lastCharPos++)
                if (Char.IsNumber(cellReference[lastCharPos]))
                    return cellReference.Substring(0, lastCharPos);

            throw new ArgumentOutOfRangeException("cellReference");
        }

Код работает для: 1. Этот код читает пустые ячейки 2. пропускает пустые строки после завершения чтения. 3. прочитайте лист сначала в порядке возрастания 4. если файл Excel используется другим процессом, OpenXML по-прежнему считывает это.

21

WorksheetPart для чтения строк.

Лини

workbookPart.WorksheetParts.First();

получает первыйWorksheetPart коллекции, которая не обязательно должна быть первой, как вы видите ее в Microsoft Excel.

Итак, перебери всеWorksheetParts и вы должны увидеть вывод в окне консоли.

static void ReadExcelFileSAX(string fileName)
{
  using (SpreadsheetDocument spreadsheetDocument = 
                                   SpreadsheetDocument.Open(fileName, true))
  {
    WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;

    // Iterate through all WorksheetParts
    foreach (WorksheetPart worksheetPart in workbookPart.WorksheetParts)
    {          
      OpenXmlPartReader reader = new OpenXmlPartReader(worksheetPart);
      string text;
      string rowNum;
      while (reader.Read())
      {
        if (reader.ElementType == typeof(Row))
        {
          do
          {
            if (reader.HasAttributes)
            {
              rowNum = reader.Attributes.First(a => a.LocalName == "r").Value;
              Console.Write("rowNum: " + rowNum);
            }

          } while (reader.ReadNextSibling()); // Skip to the next row

          break; // We just looped through all the rows so no 
                 // need to continue reading the worksheet
        }

        if (reader.ElementType != typeof(Worksheet))
          reader.Skip(); 
      }
      reader.Close();      
    }
  }  
}

Чтобы прочитать все значения ячеек, используйте следующую функцию (все детали обработки ошибок опущены):

static void ReadAllCellValues(string fileName)
{
  using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
  {
    WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;

    foreach(WorksheetPart worksheetPart in workbookPart.WorksheetParts)
    {
      OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);

      while (reader.Read())
      {
        if (reader.ElementType == typeof(Row))
        {
          reader.ReadFirstChild();

          do
          {
            if (reader.ElementType == typeof(Cell))
            {
              Cell c = (Cell)reader.LoadCurrentElement();

              string cellValue;

              if (c.DataType != null && c.DataType == CellValues.SharedString)
              {
                SharedStringItem ssi = workbookPart.SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAt(int.Parse(c.CellValue.InnerText));

                cellValue = ssi.Text.Text;
              }
              else
              {
                cellValue = c.CellValue.InnerText;
              }

              Console.Out.Write("{0}: {1} ", c.CellReference, cellValue);
            }
          } while (reader.ReadNextSibling());
          Console.Out.WriteLine();
        }            
      }
    }   
  }
}

В приведенном выше коде вы видите ячейки с типом данныхSharedString должен обрабатываться с помощьюSharedStringTablePart.

Ну, это сработало - вроде. Оказывается, по каким-то причинам рабочие листы нумеруются в обратном порядке (поэтому первый из трех моих листов на самом деле является индексом 3). Следующая проблема> Я не могу понять, как захватить строку и проверить ее содержимое. Это может быть просто, но я занимаюсь этим уже более 7 часов, и мой мозг умирает ... Argent
@ Argent: мой ответ обновлен функцией чтения всех значений ячеек из листов, включенных в файл Excel. Hans
@ Ганс, если у вас пустая ячейка, она не поднимается и действует так, как будто столбцов меньше, чем в оригинале. Как я могу прочитать пустые или нулевые ячейки? Jason Foglia
Благодарность! Это то, что я понял самостоятельно, но твоя версия выглядит менее грязной. Сегодня я немного поиграюсь с этим, и обязательно побеспокою вас, если у меня возникнут еще вопросы. И пока мы занимаемся этим, видели ли вы какое-нибудь приличное руководство / руководство по использованию OpenXML? Я пытаюсь понять это по ходу дела, и это может быть ... контрпродуктивно. Argent

Похожие вопросы