2012-05-11 3 views
12

나는 (겉으로보기에) 기본적으로 StackOverflow에 의존하는 것을 싫어하지만 지난 몇 시간 동안 Microsoft와 싸워 왔으며 막 다른 골목을 치고있는 것처럼 보입니다. 나는 (큰) Excel 2007+ 스프레드 시트를 읽으려고 노력하고 있으며, Google은 OpenXml SDK를 사용하는 것이 꽤 대중적인 선택이라고 친절하게 알려주었습니다. 그래서 나는 그 일을 포기하고, 튜토리얼을 읽고, 마이크로 소프트 자신의 라이브러리 페이지를 확인하고, 그 중 거의 아무것도 얻지 못했습니다.OpenXmlReader 사용하기

하나의 숫자 열과 하나의 문자열 만 가진 작은 테스트 스프레드 시트를 사용하고 있습니다. 대규모 테스트는 나중에 나옵니다. 내가 게시하려고하는 것과 유사한 몇 가지 구현을 시도했지만 그 중 어느 것도 데이터를 읽지 않습니다. 아래의 코드는 대부분 다른 StackOverflow 스레드에서 가져온 것입니다. 나는 내가 여러분에게 오늘이 글을 쓰는 것보다 덜 부러울 것이기 때문에 내가 여러분에게이 버전을 가지고/debug/help를 할 것이라고 생각했다.

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

그리고 부수적으로 OpenXml SDK 사용에 대한 대안이 있습니까?

+0

오픈 XML의 SDK를 여기에 사용할 수 2.0 생산성 도구를 사용해보십시오. * xlsx *를 열고 구조를 보거나 파일을 다시 만들 수있는 C# 코드를 볼 수 있습니다. 이렇게하면 작업중인 파일에서 도달 할 값의 위치를 ​​볼 수 있습니다. –

답변

18

행을 읽는 데 잘못 걸린 것 같아요. WorksheetPart.

라인

workbookPart.WorksheetParts.First(); 

에서는 Microsoft Excel에서 보는 바와 같이 반드시 첫 번째 워크 시트가 아니어야 컬렉션의 첫 번째 WorksheetPart를 얻을 수 있습니다.

모든 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를 사용하는 것이 .

+0

글쎄, 그건 일종의. 어떤 이유로 워크 시트가 역순으로 열거됨을 알 수 있습니다 (따라서 세 번째 시트의 첫 번째 시트는 실제로 인덱스 3입니다). 다음 문제> 행을 캡처하고 그 내용을 검사하는 방법을 알아낼 수 없습니다.그것은 간단 할 수도 있지만, 지금 나는 7 시간 이상 있었고 두뇌는 죽어 가고 있습니다. ... – Argent

+1

@Argent : Excel 파일에 포함 된 워크 시트의 모든 셀 값을 읽는 기능으로 답을 업데이트했습니다. – Hans

+0

감사! 이것은 내가 독자적으로 알아 낸 것의 종류이지만, 당신의 버전은 덜 지저분 해 보입니다. 오늘 잠시 그걸 가지고 놀고, 더 이상 궁금한 점이 있으면 귀찮게 할 것입니다. OpenXML을 사용하는 방법에 대한 지침이나 안내서를 본 적이 있습니까? 나는 내가 가면서 생각하고있는 종류 다. 그리고 그것은 비생산적 일 수있다. – Argent

0

빈 셀을 읽으려면 행 판독기 외부에서 할당 된 변수를 사용하고 while 루프에서 각 셀 읽기 후에 증가하는 열 인덱스가 내 변수보다 크거나 같은지 확인하고 있습니다. 이것이 일치하지 않으면 원하는 값으로 칼럼을 채우고 있습니다. 이것은 필자가 존경하는 칼럼 값에 빈 셀을 따라 잡는 데 사용한 트릭입니다. 여기에 코드입니다 :이 코드는 빈 셀이 2. 완전한 읽은 후 빈 행을 건너 뛰고 읽어 1 :

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

코드가 작동합니다. 3. 엑셀 파일은 다른 프로세스되는 경우의 OpenXML 여전히 그 판독 4. 오름차순 제의 시트를 읽는다. http://www.microsoft.com/en-us/download/details.aspx?id=5124 :