2010-01-14 4 views
0

J'ai besoin de lire (et d'analyser) de grands fichiers tableurs (20-50MB) en utilisant les bibliothèques openxml et il ne semble pas y avoir un moyen de diffuser les lignes une à la fois pour l'analyse.lire une grande feuille de calcul xml ouverte

Je reçois constamment des exceptions de mémoire insuffisante car il semble que dès que j'essaie d'accéder à une ligne (ou itérer) le contenu entier de la ligne est chargé (100K + lignes).

chacun des appels, que ce soit Elements.Where (avec requête) ou descendants() semblent charger l'ensemble rowset

est-il un moyen de diffuser ou tout simplement lire une ligne à la fois?

thx

Répondre

3

J'ai trouvé une réponse. Si vous utilisez OpenXmlReader dans la partie de la feuille de calcul, vous pouvez passer en revue et charger efficacement les éléments que vous rencontrez.

OpenXmlReader oxr = OpenXmlReader.Create(worksheetPart); 

look pour

ElementType == typeof(SheetData) 

et charger la rangée (paresseux)

Row row = (Row)oxr.LoadCurrentElement(); 
+0

Ceci. est. vite. – kerem

0

les bibliothèques OpenXML ne pas utiliser des modèles DOM ou SAX? Avec dom, vous devez généralement conserver en mémoire tout le document en entier, mais avec sax, vous pouvez diffuser les événements comme ils viennent.

+0

Les deux options sont disponibles. L'OpenXmlReader est "similaire à SAX" et nécessite beaucoup moins de mémoire. –

0

Voici le code pour lire un grand fichier Excel avec plusieurs feuilles en utilisant l'approche SAX:

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"); 
     } 

Code fonctionne pour: 1. lire les feuilles en commençant par l'ordre croissant 2. si le fichier excel est en train d'être traité par un autre processus, OpenXML le lit toujours. 3. Ce code lit les cellules vides 4. ignore les lignes vides après lecture terminée. 5. il lit 5000 lignes en 4 secondes.

Questions connexes