2017-07-04 8 views
0

Excel DocumentFormat.OpenXml SDKを使用して、Excelファイルからデータを読み取ります。 そうしている間、セルに空白の値があるかどうかを考慮しています(「はい」の場合はそれも読んでください)。Microsoft DocumentFormat.OpenXml SDKを使用してC#でExcelファイルを読み込む

workSheet.SheetDimensionがnullのExcelシートの1つに問題があり、コードが例外をスローしています。使用

コード:

クラスOpenXMLHelper {// のOpenXMLを使用してExcelファイルを開くと、1 //ワークシートのデータをすべて含むのDataTableを返すヘルパー関数。 // // OLEDBを使用してExcelデータを読み込む際に問題が発生しました(たとえば、新しいサーバーにACEドライバが存在しなくなった、 //セキュリティ上の問題によりOLEDBが動作しなくなり、ワークシートの上部)、これはより多くの //安定したデータの読み込み方法です。 //

public static DataTable ExcelWorksheetToDataTable(string pathFilename) 
    { 
     try 
     { 
      DataTable dt = new DataTable(); 
      string dimensions = string.Empty; 

      using (FileStream fs = new FileStream(pathFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
      { 
       using (SpreadsheetDocument document = SpreadsheetDocument.Open(fs, false)) 
       { 
        // Find the sheet with the supplied name, and then use that 
        // Sheet object to retrieve a reference to the first worksheet. 
        //Sheet theSheet = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName).FirstOrDefault(); 
        //--Sheet theSheet = document.WorkbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(); 

        //--if (theSheet == null) 
        //-- throw new Exception("Couldn't find the worksheet: "+ theSheet.Id); 

        // Retrieve a reference to the worksheet part. 
        //WorksheetPart wsPart = (WorksheetPart)(document.WorkbookPart.GetPartById(theSheet.Id)); 
        //--WorksheetPart wsPart = (WorksheetPart)(document.WorkbookPart.GetPartById(theSheet.Id)); 

        WorkbookPart workbookPart = document.WorkbookPart; 
        WorksheetPart wsPart = workbookPart.WorksheetParts.FirstOrDefault(); 
        Worksheet workSheet = wsPart.Worksheet; 

        dimensions = workSheet.SheetDimension.Reference.InnerText;  // Get the dimensions of this worksheet, eg "B2:F4" 

        int numOfColumns = 0; 
        int numOfRows = 0; 
        CalculateDataTableSize(dimensions, ref numOfColumns, ref numOfRows); 
        //System.Diagnostics.Trace.WriteLine(string.Format("The worksheet \"{0}\" has dimensions \"{1}\", so we need a DataTable of size {2}x{3}.", worksheetName, dimensions, numOfColumns, numOfRows)); 

        SheetData sheetData = workSheet.GetFirstChild<SheetData>(); 
        IEnumerable<Row> rows = sheetData.Descendants<Row>(); 

        string[,] cellValues = new string[numOfColumns, numOfRows]; 

        int colInx = 0; 
        int rowInx = 0; 
        string value = ""; 
        SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart; 

        // Iterate through each row of OpenXML data, and store each cell's value in the appropriate slot in our [,] string array. 
        foreach (Row row in rows) 
        { 
         for (int i = 0; i < row.Descendants<Cell>().Count(); i++) 
         { 
          // *DON'T* assume there's going to be one XML element for each column in each row... 
          Cell cell = row.Descendants<Cell>().ElementAt(i); 
          if (cell.CellValue == null || cell.CellReference == null) 
           continue;      // eg when an Excel cell contains a blank string 

          // Convert this Excel cell's CellAddress into a 0-based offset into our array (eg "G13" -> [6, 12]) 
          colInx = GetColumnIndexByName(cell.CellReference);    // eg "C" -> 2 (0-based) 
          rowInx = GetRowIndexFromCellAddress(cell.CellReference) - 1;  // Needs to be 0-based 

          // Fetch the value in this cell 
          value = cell.CellValue.InnerXml; 
          if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString) 
          { 
           value = stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText; 
          } 

          cellValues[colInx, rowInx] = value; 
         } 
        } 

        // Copy the array of strings into a DataTable. 
        // We don't (currently) make any attempt to work out which columns should be numeric, rather than string. 
        for (int col = 0; col < numOfColumns; col++) 
        { 
         //dt.Columns.Add("Column_" + col.ToString()); 
         dt.Columns.Add(cellValues[col, 0]); 
        } 

        //foreach (Cell cell in rows.ElementAt(0)) 
        //{ 
        // dt.Columns.Add(GetCellValue(doc, cell)); 
        //} 


        for (int row = 0; row < numOfRows; row++) 
        { 
         DataRow dataRow = dt.NewRow(); 
         for (int col = 0; col < numOfColumns; col++) 
         { 
          dataRow.SetField(col, cellValues[col, row]); 
         } 
         dt.Rows.Add(dataRow); 
        } 

        dt.Rows.RemoveAt(0); 
        //#if DEBUG 
        //    // Write out the contents of our DataTable to the Output window (for debugging) 
        //    string str = ""; 
        //    for (rowInx = 0; rowInx < maxNumOfRows; rowInx++) 
        //    { 
        //     for (colInx = 0; colInx < maxNumOfColumns; colInx++) 
        //     { 
        //      object val = dt.Rows[rowInx].ItemArray[colInx]; 
        //      str += (val == null) ? "" : val.ToString(); 
        //      str += "\t"; 
        //     } 
        //     str += "\n"; 
        //    } 
        //    System.Diagnostics.Trace.WriteLine(str); 
        //#endif 
        return dt; 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      return null; 
     } 

    } 

    public static void CalculateDataTableSize(string dimensions, ref int numOfColumns, ref int numOfRows) 
    { 
     // How many columns & rows of data does this Worksheet contain ? 
     // We'll read in the Dimensions string from the Excel file, and calculate the size based on that. 
     //  eg "B1:F4" -> we'll need 6 columns and 4 rows. 
     // 
     // (We deliberately ignore the top-left cell address, and just use the bottom-right cell address.) 
     try 
     { 
      string[] parts = dimensions.Split(':');  // eg "B1:F4" 
      if (parts.Length != 2) 
       throw new Exception("Couldn't find exactly *two* CellAddresses in the dimension"); 

      numOfColumns = 1 + GetColumnIndexByName(parts[1]);  // A=1, B=2, C=3 (1-based value), so F4 would return 6 columns 
      numOfRows = GetRowIndexFromCellAddress(parts[1]); 
     } 
     catch 
     { 
      throw new Exception("Could not calculate maximum DataTable size from the worksheet dimension: " + dimensions); 
     } 
    } 

    public static int GetRowIndexFromCellAddress(string cellAddress) 
    { 
     // Convert an Excel CellReference column into a 1-based row index 
     // eg "D42" -> 42 
     //  "F123" -> 123 
     string rowNumber = System.Text.RegularExpressions.Regex.Replace(cellAddress, "[^0-9 _]", ""); 
     return int.Parse(rowNumber); 
    } 

    public static int GetColumnIndexByName(string cellAddress) 
    { 
     // Convert an Excel CellReference column into a 0-based column index 
     // eg "D42" -> 3 
     //  "F123" -> 5 
     var columnName = System.Text.RegularExpressions.Regex.Replace(cellAddress, "[^A-Z_]", ""); 
     int number = 0, pow = 1; 
     for (int i = columnName.Length - 1; i >= 0; i--) 
     { 
      number += (columnName[i] - 'A' + 1) * pow; 
      pow *= 26; 
     } 
     return number - 1; 
    } 
}[enter image description here][1] 

答えて

0

SheetDimension部分はオプションです(そしてそのためのあなたはいつもそれが最新のものに頼ることはできません)。

18.3.1.35寸法(ワークシート寸法)

この要素は、ワークシートの使用範囲を指定:OpenXMLの仕様の以下の部分を参照してください。これは、ワークシートのセルを使用した の行と列の境界を指定します。 これはオプションであり、必須ではありません。。 使用されているセルには、式、テキストコンテンツ、およびセル の書式のセルが含まれます。列全体が書式設定されている場合は、その列の最初のセルが とみなされます。

SheetDimensionの部分がないExcelファイルは完全に有効なので、Excelファイルには存在しないようにしてください。

そのため私は、(代わりに、行/列の数を取得するにはSheetDimensions一部を読んで)単にSheetData一部に含まれるすべてのの要素を解析し、行数を「カウント」することをお勧めしたいです。この方法では、Excelファイルにデータの間に完全に空白の行が含まれることも考慮に入れることができます。

+0

行を解析するだけでは、必要な結果がフェッチされません。 問題: 行1:5セル(すべて値を持つ) 行2:6セル(セル1,2は空白です)。 何が起こっているのかは、データを置く間に行1はそのまま出力されますが、行2は2つの空白セルがあり、データテーブルに挿入している間に左にシフトします。このため、私はSheetDimensionを考慮していました。 これを解決する方法の提案。 コードスニペットにより明確になります。 –

関連する問題