Решение , предложенное в моем комментарии выше, будет экспортировать лист Excel как файл CSV
с использованием объектов взаимодействия Excel .
Применяя это в вашем случае:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
//...
public static void ConvertExcelToCsv(
string excelFile,
string csvOutputFile,
int worksheetNumber = 1)
{
Excel.Application xlApp = null;
Excel.Workbook xlWorkBook = null;
try
{
if (!File.Exists(excelFile))
throw new FileNotFoundException(excelFile);
if (File.Exists(csvOutputFile))
File.Delete(csvOutputFile);
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(excelFile, Type.Missing, true);
var xlSheet = xlWorkBook.Worksheets[worksheetNumber] as Excel.Worksheet;
if (xlSheet is null)
throw new ArgumentException();
xlSheet.SaveAs(csvOutputFile, Excel.XlFileFormat.xlCSV);
}
catch (FileNotFoundException)
{
Console.WriteLine($"'{excelFile}' does not exist!");
}
catch (ArgumentException)
{
Console.WriteLine("The worksheet number provided does not exist.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
xlWorkBook?.Close(false);
xlApp?.Quit();
if (xlWorkBook != null) Marshal.FinalReleaseComObject(xlWorkBook);
if (xlApp != null) Marshal.FinalReleaseComObject(xlApp);
xlWorkBook = null;
xlApp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Однако TextInfo.ListSeparator будет использоваться в качестве разделителя. Если вам нужно использовать специальный символ c, например ~ , в качестве разделителя, используя тот же подход:
using System;
using System.IO;
using System.Linq;
using System.Globalization;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
//...
public static void ConvertExcelToCsv(
string excelFile,
string csvOutputFile,
int worksheetNumber = 1,
string delimiter = null)
{
Excel.Application xlApp = null;
Excel.Workbook xlWorkBook = null;
try
{
if (!File.Exists(excelFile))
throw new FileNotFoundException(excelFile);
if (File.Exists(csvOutputFile))
File.Delete(csvOutputFile);
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(excelFile, Type.Missing, true);
var xlSheet = xlWorkBook.Worksheets[worksheetNumber] as Excel.Worksheet;
if (xlSheet is null)
throw new ArgumentException();
if (delimiter is null)
delimiter = CultureInfo.CurrentCulture.TextInfo.ListSeparator;
var xlRange = xlSheet.UsedRange;
using (var sw = new StreamWriter(csvOutputFile))
//Use:
//foreach (var r in xlRange.Rows.Cast<Excel.Range>().Skip(1))
//If the first row is a header row and you want to skip it...
foreach (Excel.Range row in xlRange.Rows)
sw.WriteLine(string.Join(delimiter, row.Cells.Cast<Excel.Range>()
.Select(x => x.Value2)));
}
catch (FileNotFoundException)
{
Console.WriteLine($"'{excelFile}' does not exist!");
}
catch (ArgumentException)
{
Console.WriteLine("The worksheet number provided does not exist.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
xlWorkBook?.Close(false);
xlApp?.Quit();
if (xlWorkBook != null) Marshal.FinalReleaseComObject(xlWorkBook);
if (xlApp != null) Marshal.FinalReleaseComObject(xlApp);
xlWorkBook = null;
xlApp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Обратите внимание, что чем больше ячеек, тем больше время обработки. Однако вы получите значения такими, какие они есть, и независимо от того, есть ли на листе столбцы смешанного типа или нет.
Кроме того, вы можете попробовать способ OleDb
для более быстрой обработки. Вместо того, чтобы заполнять DataTable и снова перебирать столбцы и строки для записи строк вывода, используйте OleDbDataReader , чтобы получить значения каждой строки из Sheet, объединить и разделить их разделителем и передать строка к методу SteamWriter.WriteLine :
using System;
using System.IO;
using System.Linq;
using System.Data.OleDb;
using System.Globalization;
//...
public static void ConvertExcelToCsv(
string excelFile,
string csvOutputFile,
int worksheetNumber = 1,
string delimiter = null)
{
try
{
if (!File.Exists(excelFile))
throw new FileNotFoundException(excelFile);
if (File.Exists(csvOutputFile))
File.Delete(csvOutputFile);
if (delimiter is null)
delimiter = CultureInfo.CurrentCulture.TextInfo.ListSeparator;
var cnnStr = $"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={excelFile};" +
$"Extended Properties='Excel 8.0;HDR=Mo;IMEX=1;'";
using (var cnn = new OleDbConnection(cnnStr))
{
cnn.Open();
var schemaTable = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (schemaTable.Rows.Count < worksheetNumber)
throw new ArgumentException();
var worksheet = schemaTable.Rows[worksheetNumber - 1]["table_name"]
.ToString().Replace("'", "");
using (var cmd = new OleDbCommand($"SELECT * FROM [{worksheet}]", cnn))
using (var r = cmd.ExecuteReader())
using (var sw = new StreamWriter(csvOutputFile))
while (r.Read())
{
var values = new object[r.FieldCount];
r.GetValues(values);
sw.WriteLine(string.Join(delimiter, values));
}
}
}
catch (FileNotFoundException)
{
Console.WriteLine($"'{excelFile}' does not exist!");
}
catch (ArgumentException)
{
Console.WriteLine("The worksheet number provided does not exist.");
}
catch (OleDbException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Вызывающий:
void TheCaller()
{
var xlFile = "XlsFile.xls";
var csvFile = Path.ChangeExtension(xlFile, "csv");
var delimiter = "~";
var sheetNumber = 1;
Console.WriteLine("Exporting...");
//ConvertExcelToCsv(xlFile, csvFile, sheetNumber); //To call the first code snippet.
ConvertExcelToCsv(xlFile, csvFile, sheetNumber, delimiter);
Console.WriteLine("Done...!");
}
Или вам может потребоваться асинхронный вызов, особенно для первых двух вариантов:
async void TheCaller()
{
var xlFile = "XlsFile.xls";
var csvFile = Path.ChangeExtension(xlFile, "csv");
var delimiter = "~";
var sheetNumber = 1;
Console.WriteLine("Exporting...");
await Task.Run(() => ConvertExcelToCsv(xlFile, csvFile, sheetNumber, delimiter));
Console.WriteLine("Done...!");
}
Примечания
1. Чтобы использовать Excel Interop Objets, добавьте ссылку на Microsoft.Office.Interop.Excel
2. Вы можете проверить это out.