我有一个类对象列表,它反过来包含另一个类对象的列表。它们看起来是这样的:
public class Column
{
public string ColName { get; set; }
public List<Item> ItemList { get; set; }
}
public class Item
{
public DateTime TimeStamp { get; set; }
public double Value { get; set; }
}
它们有两个重要的属性:
所有列中的
Length
of List<Item> ItemList
都是相同的。但是,直到运行时,我才知道这个长度是多少。,
TimeStamp
的实际值都是相同的。换句话说,每列中的TimeStamp
列表都是相同的.如果查看我为创建Column
对象列表而编写的下面的模拟函数,您将得到一个清晰的图片。这是对我的实际数据(从程序的其他地方获得)的准确描述:
private static List<Column> GetColumns()
{
var dt1 = DateTime.Now;
var dt2 = dt1.AddSeconds(1);
var dt3 = dt2.AddSeconds(1);
var dt4 = dt3.AddSeconds(1);
var col1 = new Column()
{
ColName = "ABC",
ItemList = new List<Item>
{
new Item() { TimeStamp = dt1, Value = 1 },
new Item() { TimeStamp = dt2, Value = 2 },
new Item() { TimeStamp = dt3, Value = 3 },
new Item() { TimeStamp = dt4, Value = 4 }
}
};
var col2 = new Column()
{
ColName = "XYZ",
ItemList = new List<Item>
{
new Item() { TimeStamp = dt1, Value = 4 },
new Item() { TimeStamp = dt2, Value = 3 },
new Item() { TimeStamp = dt3, Value = 2 },
new Item() { TimeStamp = dt4, Value = 1 }
}
};
var col3 = new Column()
{
ColName = "KLM",
ItemList = new List<Item>
{
new Item() { TimeStamp = dt1, Value = 1 },
new Item() { TimeStamp = dt2, Value = 2 },
new Item() { TimeStamp = dt3, Value = 4 },
new Item() { TimeStamp = dt4, Value = 8 }
}
};
var list = new List<Column>
{
col1,
col2,
col3,
};
return list;
}
一个重要的注意事项是,直到运行时我才知道List<Column>
的长度,在运行时之前我也不会知道它们中ItemList
的长度。此外,在运行时之前,我不会知道每一列的名称。现在,我的目标是将这些信息写入CSV
文件中,格式如下。
我觉得dynamic
是这里的必经之路,所以我从以下几个方面开始:
var columns = GetColumns();
var timeStamps = columns.First().ItemList.Select(x => x.TimeStamp).ToList();
var writeList = new List<dynamic>();
for (int i = 0; i < timeStamps.Count; i++)
{
dynamic csvItem = new ExpandoObject();
csvItem.TimeStamp = timeStamps[i];
// How to get columns?
writeList.Add(csvItem);
}
using (var writer = new StreamWriter("output.csv"))
{
using (var csv = new CsvHelper.CsvWriter(writer))
{
csv.WriteRecords(writeList);
}
}
这是一个开始,但由于我不知道我将拥有的列数和它们的名称,所以我不知道如何从这里开始。在这里使用dynamic
不是一种选择吗?
或者,我可以完全放弃使用CSVHelper
,从头开始写一些东西,如下所示,但是它有点混乱。我在列列表中迭代两次,总共循环三次。我正在寻找一个更优雅的解决方案,如果可能的话。
var columns = GetColumns();
var timeStamps = columns.First().ItemList.Select(x => x.TimeStamp).ToList();
var headers = "TimeStamp";
foreach (var col in columns)
{
headers += "," + col.ColName;
}
using (var fs = new FileStream("output.csv", FileMode.Create, FileAccess.Write))
{
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(headers);
for (int i = 0; i < timeStamps.Count; i++)
{
var line = timeStamps[i].ToString("yyyy/MM/dd HH:mm:ss");
foreach (var col in columns)
{
line += "," + col.ItemList[i].Value;
}
sw.WriteLine(line);
}
}
}
发布于 2019-10-02 13:16:02
对于您的需求,这可能是使用CsvHelper所能做的最好的事情。
using (var writer = new StreamWriter("output.csv"))
{
using (var csv = new CsvHelper.CsvWriter(writer))
{
var columns = GetColumns();
// Write header
csv.WriteField("TimeStamp");
foreach (var column in columns)
{
csv.WriteField(column.ColName);
}
csv.NextRecord();
// Write rows
for (int i = 0; i < columns[0].ItemList.Count; i++)
{
csv.WriteField(columns[0].ItemList[i].TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));
foreach (var column in columns)
{
csv.WriteField(column.ItemList[i].Value);
}
csv.NextRecord();
}
}
}
发布于 2019-10-02 16:43:11
下面是一个使用dynamic
的版本。
var columns = GetColumns();
var writeList = new List<dynamic>();
for (int i = 0; i < columns[0].ItemList.Count; i++)
{
var csvItem = new ExpandoObject() as IDictionary<string, object>;
csvItem.Add("TimeStamp", columns[0].ItemList[i].TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));
foreach (var column in columns)
{
csvItem.Add(column.ColName, column.ItemList[i].Value);
}
writeList.Add(csvItem);
}
using (var writer = new StreamWriter("output.csv"))
{
using (var csv = new CsvHelper.CsvWriter(writer))
{
csv.WriteRecords(writeList);
}
}
发布于 2019-10-03 22:56:44
下面是使用StringBuilder处理CSV编写的方法。此代码适用于只需将对象转换为csv的情况。但是,如果您需要使用csv作为持久化机制(无论如何,使用mongoDb或Sqlite都是疯狂的),您将不得不向这些扩展方法添加更多的功能。
分隔符:文件类型为CSV -逗号分隔值。所以这个代码使用",“。如果需要,可以通过对此代码进行一些简单的修改来创建带有分隔符的TAB。
用法:
var columns = GetColumns();
Console.WriteLine(columns.ToCsv());
//OR
columns.SaveAsCsv(@"c:\columns.csv");
Csv扩展;
public static class ColumnExtensions
{
public static void SaveAsCsv(this List<Column> columns, string filePath)
{
File.WriteAllText(columns.ToCsv(), filePath);
}
public static string ToCsv(this List<Column> columns)
{
var csv = new StringBuilder();
// Write as an expression or simply
//csv.AppendCsvHeader(nameof(Item.TimeStamp));
csv.AppendCsvHeader<string, DateTime>(x => columns.First().ItemList.First().TimeStamp);
for (var index = 0; index < columns.Count; index++)
{
var column = columns[index];
// Most csv readers don't care if you have a "," at the end of the line. But for completeness we avoid doing that.
// It makes the code a bit more complicated though. You can ignore this you want.
csv.AppendCsvHeader(column.ColName, index == columns.Count - 1);
}
csv.AppendLine();
for (var i = 0; i < columns[0].ItemList.Count; i++)
{
csv.AppendCsvField(columns[0]
.ItemList[i]
.TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));
for (var index = 0; index < columns.Count; index++)
{
var column = columns[index];
csv.AppendCsvField(column.ItemList[i]
.Value.ToString("N"), index == columns.Count - 1);
}
csv.AppendLine();
}
return csv.ToString();
}
}
public static class CsvExtensions
{
private const string Delimiter = ",";
private static string AsCsvFriendly(this string val)
{
return val?.Replace(",", ";") ?? string.Empty;
}
private static string AddDelimiterIfRequired(bool withoutDelimiter)
{
return withoutDelimiter ? string.Empty : Delimiter;
}
public static void AppendCsvField(this StringBuilder stringBuilder, string value, bool withoutDelimiter = false)
{
stringBuilder.Append($"{value.AsCsvFriendly()}{AddDelimiterIfRequired(withoutDelimiter)}");
}
public static void AppendCsvHeader(this StringBuilder stringBuilder, string value, bool withoutDelimiter = false)
{
stringBuilder.Append($"{value.AsCsvFriendly()}{AddDelimiterIfRequired(withoutDelimiter)}");
}
public static void AppendCsvHeader<TIn, TOut>(this StringBuilder stringBuilder, Expression<Func<TIn, TOut>> f, bool withoutDelimiter = false)
{
stringBuilder.Append($"{(f.Body as MemberExpression)?.Member.Name}{AddDelimiterIfRequired(withoutDelimiter)}");
}
}
https://stackoverflow.com/questions/58192528
复制相似问题