2011-08-26 98 views
4

我有一个要求将Excel(2010)文件转换为csv。目前我使用Excel Interop打开并保存CSV文件,效果很好。然而,Interop在我们使用它的环境中存在一些问题,所以我正在寻找另一种解决方案。如何使用OpenXML SDK将Excel转换为CSV?

我发现在没有互操作的情况下使用Excel文件的方式是使用OpenXML SDK。我将一些代码组合在一起以遍历每张表中的所有单元格,并简单地将它们写入另一个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(',')); 
      } 
     } 
    } 
} 

如果我有下面的Excel文件数据:

one,two,three 
,, 
last,,row 

我会得到下面的CSV(这是错误的):

one,two,three 
last,row 

回答

3

我不认为OPENXML是这个问题的正确工具。我会建议从表with an OleDbConnection中获取数据,然后使用this方法将其转化为csv文件。

一旦你在内存中的数据表中获得了数据,你就可以更好地控制这种情况。

+0

不使用与OleDbConnection已安装需要的Excel? Open XML SDK的优点是不需要Excel。 –

+0

不需要,不需要。它会将文件视为一个二进制数据存储,它就是这样。由于某种原因,我最终每两到三年就会这样做:)。 –

+0

我还应该注意到,我做了很多OpenXML工作......这将是一个使用核弹杀死蚊子的情况。 –

3

您可以使用OLEDB连接和查询excel文件,转换行csv格式,并将结果保存到文件

这里是一个简单的例子,我测试了这个 它创建一个不同的csv文件采用Unicode编码,制表符分隔每个表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