有没有简单的方法将.xls文件转换为.csv文件?(Excel)

57

有没有简单的方法将 .xls 文件转换为 .csv 文件?(Excel)

使用 C# 代码?

我是指将现有的 .xls 文件转换为 .csv 文件。


还有这个stackoverflow的问答 https://dev59.com/t7Xna4cB1Zd3GeqPT_9o - andrew pate
9个回答

33

下面是一个C#的方法来实现此功能。请记得添加自己的错误处理 - 这主要是为了简洁起见假设一切正常工作。这只适用于4.0+框架,但这主要是因为可选的worksheetNumber参数。如果您需要支持早期版本,则可以重载该方法。

static void ConvertExcelToCsv(string excelFilePath, string csvOutputFile, int worksheetNumber = 1) {
   if (!File.Exists(excelFilePath)) throw new FileNotFoundException(excelFilePath);
   if (File.Exists(csvOutputFile)) throw new ArgumentException("File exists: " + csvOutputFile);

   // connection string
   var cnnStr = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;IMEX=1;HDR=NO\"", excelFilePath);
   var cnn = new OleDbConnection(cnnStr);

   // get schema, then data
   var dt = new DataTable();
   try {
      cnn.Open();
      var schemaTable = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
      if (schemaTable.Rows.Count < worksheetNumber) throw new ArgumentException("The worksheet number provided cannot be found in the spreadsheet");
      string worksheet = schemaTable.Rows[worksheetNumber - 1]["table_name"].ToString().Replace("'", "");
      string sql = String.Format("select * from [{0}]", worksheet);
      var da = new OleDbDataAdapter(sql, cnn);
      da.Fill(dt);
   }
   catch (Exception e) {
      // ???
      throw e;
   }
   finally {
      // free resources
      cnn.Close();
   }

   // write out CSV data
   using (var wtr = new StreamWriter(csvOutputFile)) {
      foreach (DataRow row in dt.Rows) {
         bool firstLine = true;
         foreach (DataColumn col in dt.Columns) {
            if (!firstLine) { wtr.Write(","); } else { firstLine = false; }
            var data = row[col.ColumnName].ToString().Replace("\"", "\"\"");
            wtr.Write(String.Format("\"{0}\"", data));
         }
         wtr.WriteLine();
      }
   }
}

31

查看Excel对象中 .SaveAs() 方法。

wbWorkbook.SaveAs("c:\yourdesiredFilename.csv", Microsoft.Office.Interop.Excel.XlFileFormat.xlCSV)

或者以下方式:

public static void SaveAs()
{
    Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
    Microsoft.Office.Interop.Excel.Workbook wbWorkbook = app.Workbooks.Add(Type.Missing);
    Microsoft.Office.Interop.Excel.Sheets wsSheet = wbWorkbook.Worksheets;
    Microsoft.Office.Interop.Excel.Worksheet CurSheet = (Microsoft.Office.Interop.Excel.Worksheet)wsSheet[1];

    Microsoft.Office.Interop.Excel.Range thisCell = (Microsoft.Office.Interop.Excel.Range)CurSheet.Cells[1, 1];

    thisCell.Value2 = "This is a test.";

    wbWorkbook.SaveAs(@"c:\one.xls", Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlShared, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    wbWorkbook.SaveAs(@"c:\two.csv", Microsoft.Office.Interop.Excel.XlFileFormat.xlCSVWindows, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlShared, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

    wbWorkbook.Close(false, "", true);
}

1
谢谢你的帮助,但我如何转换一个已存在的文件? - Gold
如果你还在疑惑,你需要首先使用Excel.Application.Workbooks.Open()方法将现有文件作为工作簿打开,并将其用作wbWorkbook参数。 - markdigi
1
使用xlSaveAsAccessMode.xlNoChange更可靠(如果我使用xlShared,则调用SaveAs会崩溃)。 - Benlitz
3
我发现这个答案有一个缺陷。如果你在文件中包含了外语字符(例如中文),你需要将第二个参数设置为XlUnicodeText,这样可以保留这些字符,但是CSV格式会丢失,它会被保存为制表符分隔的格式。在末尾附近有一个参数可以设置代码页,然而经过搜索互联网,似乎没有人知道如何使其正常工作。 - mj_
你应该在结尾处抛出 app.quit() 来关闭 Excel 实例。 - Bob Probst
1
Interop类型“ApplicationClass”无法嵌入?只需将“ApplicationClass”替换为“Application”。 - Juan Carlos Puerto

19

安装这两个软件包

<packages>
  <package id="ExcelDataReader" version="3.3.0" targetFramework="net451" />
  <package id="ExcelDataReader.DataSet" version="3.3.0" targetFramework="net451" />
</packages>

辅助函数

using ExcelDataReader;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExcelToCsv
{
    public class ExcelFileHelper
    {
        public static bool SaveAsCsv(string excelFilePath, string destinationCsvFilePath)
        {

            using (var stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                IExcelDataReader reader = null;
                if (excelFilePath.EndsWith(".xls"))
                {
                    reader = ExcelReaderFactory.CreateBinaryReader(stream);
                }
                else if (excelFilePath.EndsWith(".xlsx"))
                {
                    reader = ExcelReaderFactory.CreateOpenXmlReader(stream);
                }

                if (reader == null)
                    return false;

                var ds = reader.AsDataSet(new ExcelDataSetConfiguration()
                {
                    ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
                    {
                        UseHeaderRow = false
                    }
                });

                var csvContent = string.Empty;
                int row_no = 0;
                while (row_no < ds.Tables[0].Rows.Count)
                {
                    var arr = new List<string>();
                    for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
                    {
                        arr.Add(ds.Tables[0].Rows[row_no][i].ToString());
                    }
                    row_no++;
                    csvContent += string.Join(",", arr) + "\n";
                }
                StreamWriter csv = new StreamWriter(destinationCsvFilePath, false);
                csv.Write(csvContent);
                csv.Close();
                return true;
            }
        }
    }
}

使用方法:

var excelFilePath = Console.ReadLine();
string output = Path.ChangeExtension(excelFilePath, ".csv");
ExcelFileHelper.SaveAsCsv(excelFilePath, output);

我尝试使用这个库,但遇到了一个障碍,在这里提出:https://github.com/ExcelDataReader/ExcelDataReader/issues/350 - Ruslan
ExcelDataReader对我来说运行得非常好,但是如果您要将大型Excel文件转换为CSV,我建议您避免使用AsDataSet将整个数据集读入内存,而是逐行读取数据并管理输出,以避免在内存中存储大量数据。如何做到这一点的一个很好的例子可以在他们的维基上找到,链接在此https://github.com/ExcelDataReader/ExcelDataReader#how-to-use - M3SSYM4RV1N
非常感谢这个伟大的函数。我有一个以XLSX格式表示的日期“10/08/2018”,但是当我将其转换为CSV格式时,它会插入时间“10/08/2018 12:00:00AM”。我尝试通过设置“UseColumnDataType = false”来解决这个问题,但结果仍然相同。 - Alex Gordon
1
愚蠢的问题 - 如果你有多个工作表会发生什么? - Richard Griffiths
有了 .Xls 扩展名的文件,我想使用它,它起作用得很好,对于多个工作表,我会进行检查和更新。这个答案更理想,因为它不依赖于 Microsoft Office。 - Harish Patil
请注意,如果数据行中包含逗号,则可能需要转义。例如:"a,b"需要:arr.Add(""" + ds.Tables[0].Rows[row_no][i].ToString() + """); - MTMDev

9

在更新到Visual Studio 2022并测试了最相关的答案后,我不得不想出一个混合解决方案才能使其正常工作。

首先,我们需要安装以下Nuget软件包:ExcelDataReaderExcelDataReader.DataSetSystem.Text.Encoding.CodePages

然后,为了实现清晰的架构,请在相应的命名空间中创建一个单独的类:

using ExcelDataReader;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace YourProjectNameSpace
{
    public class ExcelFileHelper
    {

        /// <summary>
        /// Converts a given XLS into CSV file format.
        /// </summary>
        public static bool SaveAsCsv(string excelFilePath, string destinationCsvFilePath)
        {

            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            using (var stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                IExcelDataReader reader = null;
                if (excelFilePath.EndsWith(".xls"))
                {
                    reader = ExcelReaderFactory.CreateBinaryReader(stream);
                }
                else if (excelFilePath.EndsWith(".xlsx"))
                {
                    reader = ExcelReaderFactory.CreateOpenXmlReader(stream);
                }

                if (reader == null)
                    return false;

                var ds = reader.AsDataSet(new ExcelDataSetConfiguration()
                {
                    ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
                    {
                        UseHeaderRow = false
                    }
                });

                var csvContent = string.Empty;
                int row_no = 0;
                while (row_no < ds.Tables[0].Rows.Count)
                {
                    var arr = new List<string>();
                    for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
                    {
                        arr.Add(ds.Tables[0].Rows[row_no][i].ToString());
                    }
                    row_no++;
                    csvContent += string.Join(",", arr) + "\n";
                }
                StreamWriter csv = new StreamWriter(destinationCsvFilePath, false);
                csv.Write(csvContent);
                csv.Close();
                return true;
            }
        }
    }
}

请注意,在函数开始时,我必须包含这行代码:

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

如果省略上面的行,您可能会遇到以下错误:
NotSupportedException: No data is available for encoding 1252
因此,为了更好的兼容性,请确保使用它。
最后,使用示例:
var execPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)?.Replace("file:\\", "");
    
string FileNameXLS = "\\file.xls";
string FileNameCSV = "\\file.csv";
        
Console.WriteLine("Exporting file to CSV...." + "\n");
ExcelFileHelper.SaveAsCsv(execPath + FileNameXLS, execPath + FileNameCSV);
Console.WriteLine("File exported to CSV!" + "\n");

1
这个完美地运行了!当在string.Join()中用分号替换逗号时,它会产生与在Excel中使用“另存为CSV UTF-8(逗号分隔)”完全相同的结果。 - Martin H
这对我的项目非常有效,谢谢。 - Barrassment
1
谢谢。我能够轻松地修改以使用管道作为分隔符并跳过前导行。SaveAsCsv(string excelFilePath, string destinationCsvFilePath, int startRow = 0, string delimiter = ",") - CJ Edgerton

3

我需要做同样的事情。最终我得到了与Kman类似的东西。

       static void ExcelToCSVCoversion(string sourceFile,  string targetFile)
    {
        Application rawData = new Application();

        try
        {
            Workbook workbook = rawData.Workbooks.Open(sourceFile);
            Worksheet ws = (Worksheet) workbook.Sheets[1];
            ws.SaveAs(targetFile, XlFileFormat.xlCSV);
            Marshal.ReleaseComObject(ws);
        }

        finally
        {
            rawData.DisplayAlerts = false;
            rawData.Quit();
            Marshal.ReleaseComObject(rawData);
        }


        Console.WriteLine();
        Console.WriteLine($"The excel file {sourceFile} has been converted into {targetFile} (CSV format).");
        Console.WriteLine();
    }

如果有多个表格,转换后会丢失这些信息,但是您可以循环遍历每个表格,并将每个表格保存为CSV格式。

2
了解汇编引用将会很有帮助。 - Luke Vanzweden

3
这是对nate_weldon答案的修改,有一些改进:
  • 更加健壮的Excel对象释放
  • 在尝试保存前设置application.DisplayAlerts = false;以隐藏提示
另外请注意,application.Workbooks.Openws.SaveAs方法期望sourceFilePathtargetFilePath是完整路径(即目录路径+文件名)。
private static void SaveAs(string sourceFilePath, string targetFilePath)
{
    Application application = null;
    Workbook wb = null;
    Worksheet ws = null;

    try
    {
        application = new Application();
        application.DisplayAlerts = false;
        wb = application.Workbooks.Open(sourceFilePath);
        ws = (Worksheet)wb.Sheets[1];
        ws.SaveAs(targetFilePath, XlFileFormat.xlCSV);
    }
    catch (Exception e)
    {
        // Handle exception
    }
    finally
    {
        if (application != null) application.Quit();
        if (ws != null) Marshal.ReleaseComObject(ws);
        if (wb != null) Marshal.ReleaseComObject(wb);
        if (application != null) Marshal.ReleaseComObject(application);
    }
}

2

我整合了 @mattmc3 的回答。如果你想转换一个 xlsx 文件,你应该使用这个连接字符串(matt 提供的字符串适用于 xls 格式,不适用于 xlsx 格式):

最初的回答:

var cnnStr = String.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;IMEX=1;HDR=NO\"", excelFilePath);

1

我曾经遇到类似的任务问题。需要将类似xlsx格式的输入交易数据转换为制表符分隔符以便已有系统进行自动处理,并且需要无人值守运行。在搜索了多个不同网站上的多个解决方案并尝试了其中两种之后,使用像上面提到的MS Office Excel for C#的方法,并且出现了与MS Office不同版本和可能存在于PC上的旧版本产生的问题,并且对此没有任何控制。最终我选择使用...Aspose.Cells 通过NuGet来解决。这个解决方案只需要四行代码。

string soureFilePath = "my/source/path/file.xlsx";
string targetFilePath = "my/output/path/file.txt"; 
var book = new Workbook(soureFilePath);
book.Save(targetFilePath, SaveFormat.Tsv);

它只转换了第一个表格,忽略了第二个和第三个表格,但这对我的使用来说没问题。我猜它有功能可以转换所有的表格,如果需要的话,我只是不需要,所以没有深入研究。
他们的网站,如果人们想查看他们的信息或许可协议(免费使用)。
轻松操作电子表格| Aspose.Cells通用库 https://products.aspose.com/cells 注意:我不为Aspose工作,我与Aspose无关,我不从这篇文章中获得任何利益。

0

我维护一些库,使 Excel 转 CSV 转换尽可能简单:Sylvan.Data.ExcelSylvan.Data.Csv。 Sylvan.Data.Excel 可用于读取 .xlsx.xlsb.xls 文件。但是,它只能写入 .xlsx 文件。

这里是将 Excel 电子表格转换为 CSV 的最小示例:

using Sylvan.Data.Csv;
using Sylvan.Data.Excel;

using var reader = ExcelDataReader.Create("MyData.xlsx");
using var csvWriter = CsvDataWriter.Create("MyData.csv");
csvWriter.Write(reader);

这些库除了 .NET 运行库之外没有外部依赖,不需要安装 Excel。它们针对最新的受支持 .NET 版本进行优化,并且可以跨平台运行。它们也是 .NET 生态系统中最快的库。它们采用 MIT 许可证,因此可以自由使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接