2011-08-26 4 views
4

J'ai une exigence pour convertir les fichiers Excel (2010) en CSV. Actuellement, j'utilise Excel Interop pour ouvrir et SaveAs csv, qui fonctionne bien. Cependant, l'Interop a quelques problèmes dans l'environnement où nous l'utilisons, donc je suis à la recherche d'une autre solution.Comment convertir Excel en CSV en utilisant OpenXML SDK?

J'ai trouvé le moyen de travailler avec des fichiers Excel sans interopérer est d'utiliser le SDK OpenXML. J'ai rassemblé du code pour parcourir toutes les cellules de chaque feuille et les écrit simplement dans un autre fichier au format CSV.

Un problème que j'ai est la manipulation des rangées vides et des cellules. Il semble que, avec ce code, les lignes vierges et les cellules sont complètement inexistantes, donc je n'ai aucun moyen de les connaître. Y a-t-il des traces à travers toutes les rangées et toutes les cellules, y compris les blancs?

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

Si j'ai les données de fichier Excel suivant:

one,two,three 
,, 
last,,row 

Je vais obtenir le CSV suivant (ce qui est faux):

one,two,three 
last,row 

Répondre

3

Je ne pense pas OpenXml est le bon outil pour ce problème. Je recommande d'obtenir les données sur la feuille with an OleDbConnection puis dans un fichier csv avec la méthode this.

Une fois que vous avez les données dans une base de données en mémoire, vous avez beaucoup plus de contrôle sur la situation.

+0

ne pas le OleDbConnection exigent que Excel est installé? L'avantage d'Open XML SDK est qu'Excel n'est pas requis. –

+0

Non, pas nécessaire. Il traitera le fichier comme un magasin de données binaires, ce qui est le cas. Je finis par faire cela tous les deux ou trois ans, pour une raison quelconque :). –

+0

Je devrais également noter que je fais beaucoup de travail OpenXml ... ce serait un cas d'utilisation d'un nuke pour tuer un moustique. –

3

vous pouvez utiliser la connexion oledb et interroger le fichier Excel, convertir les lignes au format csv et enregistrer les résultats dans un fichier

ici est un exemple simple i testé pour cette il crée un fichier csv différent unicode encodée , délimité par des tabulations pour chaque feuille dans le fichier 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

Merci @Adam, mais ce code n'écrit pas la première ligne du fichier Excel. Je suppose qu'il est traité comme des noms de colonnes? Ce n'est pas ce que je veux. Connaissez-vous un moyen d'éviter cela? – 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

Je ne sais pas pourquoi cette réponse n'est pas au top. Le plus fiable, le plus à jour et ne nécessite aucun pilote d'installation ou d'avoir à parler aux gens de l'infrastructure mal. – user609926

Questions connexes