2011-08-26 9 views
4

Excel (2010) 파일을 CSV로 변환해야한다는 요구 사항이 있습니다. 현재 Excel Interop을 사용하여 CSAS를 열고 SaveAs를 사용하고 있습니다. 그러나 Interop은 우리가 사용하는 환경에 문제가 있으므로 다른 해결책을 찾고 있습니다.OpenXML SDK를 사용하여 Excel을 CSV로 어떻게 변환합니까?

OpenXML SDK를 사용하여 interop없이 Excel 파일로 작업하는 방법을 발견했습니다. 각 시트의 모든 셀을 반복 처리하고 CSV의 다른 파일에 코드를 씁니다.

빈 행과 셀을 처리하는 데 문제가 하나 있습니다. 이 코드에서는 빈 행과 셀이 완전히 존재하지 않으므로 그 행을 알 수있는 방법이 없습니다. 공백을 포함하여 모든 행과 셀을 반복하여 반복 할 수 있습니까?

string filename = @"D:\test.xlsx"; 
string outputDir = Path.GetDirectoryName(filename); 
//-------------------------------------------------------- 

using (SpreadsheetDocument document = SpreadsheetDocument.Open(filename, false)) 
{ 

    foreach (Sheet sheet in document.WorkbookPart.Workbook.Descendants<Sheet>()) 
    { 
     WorksheetPart worksheetPart = (WorksheetPart) document.WorkbookPart.GetPartById(sheet.Id); 
     Worksheet worksheet = worksheetPart.Worksheet; 

     SharedStringTablePart shareStringPart = document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First(); 
     SharedStringItem[] items = shareStringPart.SharedStringTable.Elements<SharedStringItem>().ToArray(); 

     // Create a new filename and save this file out. 
     if (string.IsNullOrWhiteSpace(outputDir)) 
      outputDir = Path.GetDirectoryName(filename); 
     string newFilename = string.Format("{0}_{1}.csv", Path.GetFileNameWithoutExtension(filename), sheet.Name); 
     newFilename = Path.Combine(outputDir, newFilename); 

     using (var outputFile = File.CreateText(newFilename)) 
     { 
      foreach (var row in worksheet.Descendants<Row>()) 
      { 
       StringBuilder sb = new StringBuilder(); 
       foreach (Cell cell in row) 
       { 
        string value = string.Empty; 
        if (cell.CellValue != null) 
        { 
         // If the content of the first cell is stored as a shared string, get the text 
         // from the SharedStringTablePart. Otherwise, use the string value of the cell. 
         if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString) 
          value = items[int.Parse(cell.CellValue.Text)].InnerText; 
         else 
          value = cell.CellValue.Text; 
        } 

        // to be safe, always use double quotes. 
        sb.Append(string.Format("\"{0}\",", value.Trim())); 
       } 
       outputFile.WriteLine(sb.ToString().TrimEnd(',')); 
      } 
     } 
    } 
} 

나는 다음 엑셀 파일의 데이터가있는 경우 :

one,two,three 
,, 
last,,row 

I (잘못) 다음 CSV를 얻을 것이다 :

one,two,three 
last,row 

답변

3

내가 OPENXML는 생각하지 않습니다 이 문제에 대한 올바른 도구. 나는 with an OleDbConnection 시트에서 데이터를 가져온 다음 this 메서드를 사용하여 CSV 파일로 가져 오는 것이 좋습니다.

메모리의 데이터 테이블에 데이터를 가져 오면 상황을 훨씬 더 잘 제어 할 수 있습니다.

+0

가되지는 OleDbConnection합니까 파일, 탭은 엑셀의 각 시트의 구분? Open XML SDK의 장점은 Excel이 필요하지 않다는 것입니다. –

+0

아니요, 필수 사항은 아닙니다. 파일을 이진 데이터 저장소로 취급합니다. 나는 어떤 이유로 2 ~ 3 년마다이 일을 끝낸다. :) –

+0

나는 OpenXml 작업을 많이한다는 것에 주목해야한다 ... 이것은 핵을 사용하여 모기를 죽이는 경우가 될 것이다. –

3

당신은 OLEDB 연결을 사용하여 엑셀 파일을 조회, CSV 형식으로 행을 변환 파일 여기

에 결과를 저장할 수있는 것은 내가 그것을 인코딩 된 다른 csv 파일 유니 코드를 생성이 테스트 간단한 예입니다 Excel이 설치되어 필요로하는

using System; 
using System.Collections.Generic; 
using System.Data; 
using System.Data.OleDb; 
using System.IO; 
using System.Linq; 
using System.Text; 

namespace XlsTests 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string _XlsConnectionStringFormat = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=NO;IMEX=1\""; 
      string xlsFilename = @"C:\test.xlsx"; 
      using (OleDbConnection conn = new OleDbConnection(string.Format(_XlsConnectionStringFormat, xlsFilename))) 
      { 
       try 
       { 
        conn.Open(); 

        string outputFilenameHeade = Path.GetFileNameWithoutExtension(xlsFilename); 
        string dir = Path.GetDirectoryName(xlsFilename); 
        string[] sheetNames = conn.GetSchema("Tables") 
               .AsEnumerable() 
               .Select(a => a["TABLE_NAME"].ToString()) 
               .ToArray(); 
        foreach (string sheetName in sheetNames) 
        { 
         string outputFilename = Path.Combine(dir, string.Format("{0}_{1}.csv", outputFilenameHeade, sheetName)); 
         using (StreamWriter sw = new StreamWriter(File.Create(outputFilename), Encoding.Unicode)) 
         { 
          using (DataSet ds = new DataSet()) 
          { 
           using (OleDbDataAdapter adapter = new OleDbDataAdapter(string.Format("SELECT * FROM [{0}]", sheetName), conn)) 
           { 
            adapter.Fill(ds); 

            foreach (DataRow dr in ds.Tables[0].Rows) 
            { 
             string[] cells = dr.ItemArray.Select(a => a.ToString()).ToArray(); 
             sw.WriteLine("\"{0}\"", string.Join("\"\t\"", cells)); 
            } 
           } 
          } 
         } 
        } 
       } 
       catch (Exception exp) 
       { 
        // handle exception 
       } 
       finally 
       { 
        if (conn.State != ConnectionState.Open) 
        { 
         try 
         { 
          conn.Close(); 
         } 
         catch (Exception ex) 
         { 
          // handle exception 
         } 
        } 
       } 
      } 
     } 
    } 
} 
+0

감사합니다. @Adam하지만이 코드는 Excel 파일의 첫 번째 행을 기록하지 않습니다. 컬럼 이름으로 취급 되나요? 이것은 내가 원하는 것이 아닙니다. 그걸 피하는 방법을 아십니까? – TheSean

1
//Xlsx to Csv 
ConvertXlsxToCsv(@"D:\test.xlsx", @"C:\"); 

internal static void ConvertXlsxToCsv(string SourceXlsxName, string DestinationCsvDirectory) 
{ 
    try 
    { 
     using (SpreadsheetDocument document = SpreadsheetDocument.Open(SourceXlsxName, false)) 
     { 

      foreach (Sheet _Sheet in document.WorkbookPart.Workbook.Descendants<Sheet>()) 
      { 
       WorksheetPart _WorksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(_Sheet.Id); 
       Worksheet _Worksheet = _WorksheetPart.Worksheet; 

       SharedStringTablePart _SharedStringTablePart = document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First(); 
       SharedStringItem[] _SharedStringItem = _SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ToArray(); 

       if (string.IsNullOrEmpty(DestinationCsvDirectory)) 
        DestinationCsvDirectory = Path.GetDirectoryName(SourceXlsxName); 
       string newFilename = string.Format("{0}_{1}.csv", Path.GetFileNameWithoutExtension(SourceXlsxName), _Sheet.Name); 
       newFilename = Path.Combine(DestinationCsvDirectory, newFilename); 

       using (var outputFile = File.CreateText(newFilename)) 
       { 
        foreach (var row in _Worksheet.Descendants<Row>()) 
        { 
         StringBuilder _StringBuilder = new StringBuilder(); 
         foreach (Cell _Cell in row) 
         { 
          string Value = string.Empty; 
          if (_Cell.CellValue != null) 
          { 
           if (_Cell.DataType != null && _Cell.DataType.Value == CellValues.SharedString) 
            Value = _SharedStringItem[int.Parse(_Cell.CellValue.Text)].InnerText; 
           else 
            Value = _Cell.CellValue.Text; 
          } 
          _StringBuilder.Append(string.Format("{0},", Value.Trim())); 
         } 
         outputFile.WriteLine(_StringBuilder.ToString().TrimEnd(',')); 
        } 
       } 
      } 
     } 
    } 
    catch (Exception Ex) 
    { 
     throw Ex; 
    } 
} 
+0

이 답변이 왜 맨 위에 있지 않은지 나는 알지 못합니다. 가장 믿을만하고 최신이며 드라이버를 설치하거나 악의적 인 인프라 사용자와 대화 할 필요가 없습니다. – user609926

관련 문제