Я пытаюсь прочитать документ excel и записать его как csv.
Я понял, как это сделать несколькими немного разными способами, но это чертовски медленно.
это то, что у меня есть, работает и проходит через 2 арка с 16384 строками и 5 столбцами данных примерно за 1 минуту 36 секунд.
public void ToCSV(Stream excelStream, int i)
{
// IExcelDataReader excelReader = null;
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
foreach (DataTable aSheet in excelsheets.Tables)
{
string strCSVData = "";
string sheetName = aSheet.TableName;
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
strCSVData += column.ToString().Replace(",", ",") + ",";
}
strCSVData += "\n";
}
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
csvFile.Write(strCSVData);
csvFile.Close();
}
}
}
Сейчас пытаюсь ускорить. Я был немного быстрее, используя обычный цикл for, но ничего впечатляющего — около 1 минуты 33 секунды.
Поэтому я подумал, как насчет использования Parallel.foreach вместо этого. однако это привело либо к записи только трети данных, либо к тому, что в настоящее время их нет.
вот как я изменил вышеуказанный метод.
public void ToCSVParallel(Stream excelStream, int i)
{
// IExcelDataReader excelReader = null;
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
DataTable aSheet = excelsheets.Tables[sheet];
List<string> strCSVData = new List<string>();
string sheetName = aSheet.TableName;
IEnumerable<DataRow> dataSheet = aSheet.AsEnumerable();
Parallel.ForEach<DataRow>(dataSheet, row =>
{
string strRow = "";
for (int column = 0; column < row.ItemArray.Count(); column++)
{
strRow = row[column].ToString().Replace(",", ",") + ",";
}
strRow += "\n";
strCSVData.Append(strRow);
});
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
//StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
System.IO.File.WriteAllLines(strOutputFileName, strCSVData);
// csvFile.Write(strCSVData);
//csvFile.Close();
}
}
}
Теперь я понятия не имею, что я делаю неправильно. Но я вполне уверен, что неправильно понимаю, как я могу использовать parallel.foreach, но что я делаю неправильно?
или есть лучший/умный/простой способ ускорить мой метод?
ИЗМЕНИТЬ:
Основываясь на всех ваших предложениях, я пришел к следующим изменениям.
public void ToCSVParallel(Stream excelStream, int i)
{
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
DataTable aSheet = excelsheets.Tables[sheet];
ConcurrentBag<string> strCSVData = new ConcurrentBag<string>();
string sheetName = aSheet.TableName;
IEnumerable<DataRow> dataSheet = aSheet.AsEnumerable();
Parallel.ForEach<DataRow>(dataSheet, row =>
{
StringBuilder strRow = new StringBuilder();
for (int column = 0; column < row.ItemArray.Count(); column++)
{
strRow.Append(row[column].ToString().Replace(",", ",") + ",");
}
strCSVData.Add(strRow.ToString());
});
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
System.IO.File.WriteAllLines(strOutputFileName, strCSVData);
}
}
}
Однако, основываясь на предложении @Magnus, я также изменил свой первоначальный метод на это:
public void ToCSV(Stream excelStream, int i)
{
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
foreach (DataTable aSheet in excelsheets.Tables)
{
string sheetName = aSheet.TableName;
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
using (StreamWriter csvFile = new StreamWriter(strOutputFileName, false))
{
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
csvFile.Write(column.ToString().Replace(",", ",") + ",");
}
csvFile.WriteLine();
}
}
}
}
}
и результат меня поразил.
параллель в среднем на 1000 миллисекунд медленнее, чем модифицированные циклы Foreach.
однако моя идея сделать метод более быстрым теперь находится в пределах приемлемого уровня. параллель занимает в среднем около 8800 миллисекунд. цикл foreach занимает в среднем 7600 миллисекунд. оба из них находятся на 2 ковчегах с 16384 строками и 5 столбцами данных в каждом.
Replace
или записать его в CSV? Я думаю, что оптимизации можно разделить, прежде чем мы приступим к решению проблемы. - person Svek   schedule 09.07.2018StreamWriter
вместо конкатенации вstrCSVData
. - person Magnus   schedule 09.07.2018strRow = row[column].ToString().Replace(",", ",") + ",";
. Вы имели в видуstrRow += ...
? - person Cid   schedule 09.07.2018