Answers:
框架已经为我们做的事情真令人惊奇。
List<int> myValues;
string csv = String.Join(",", myValues.Select(x => x.ToString()).ToArray());
对于一般情况:
IEnumerable<T> myList;
string csv = String.Join(",", myList.Select(x => x.ToString()).ToArray());
如您所见,实际上没有什么不同。请注意,在包含逗号的情况下x.ToString()
,可能实际上需要用引号引起来。"\"" + x.ToString() + "\""
x.ToString()
有关此变化的有趣读物:请参见Eric Lippert博客上的Comma Quibbling。
注意:这是在.NET 4.0正式发布之前编写的。现在我们可以说
IEnumerable<T> sequence;
string csv = String.Join(",", sequence);
使用重载String.Join<T>(string, IEnumerable<T>)
。此方法将自动将每个元素投影x
到x.ToString()
。
using
声明。
System.Linq.Enumerable
(当然,您需要System.Core.dll
组装,但是大概已经有了)。你看,List<int>
从未有Select
一个方法。而是System.Linq.Enumerable
将定义Select
为的扩展方法IEnumerable<T>
,是其中List<int>
的一个示例。因此,您需要System.Linq.Enumerable
在导入中选择此扩展方法。
x.ToString(CultureInfo.InvariantCulture)
。这将使用句点作为小数点分隔符。
您可以创建可在任何IEnumerable上调用的扩展方法:
public static string JoinStrings<T>(
this IEnumerable<T> values, string separator)
{
var stringValues = values.Select(item =>
(item == null ? string.Empty : item.ToString()));
return string.Join(separator, stringValues.ToArray());
}
然后,您可以仅在原始列表上调用该方法:
string commaSeparated = myList.JoinStrings(", ");
您可以使用String.Join
。
String.Join(
",",
Array.ConvertAll(
list.ToArray(),
element => element.ToString()
)
);
ConvertAll
此处调用时指定泛型类型参数-两者int
和string
都会被推断出来。
Array.ConvertAll(...' you can just do
list.ConvertAll(e => e.ToString())。ToArray)`,只需键入更少的内容即可。
如果任何机构要转换自定义类对象列表而不是字符串列表,请使用类的csv行表示形式覆盖类的ToString方法。
Public Class MyClass{
public int Id{get;set;}
public String PropertyA{get;set;}
public override string ToString()
{
return this.Id+ "," + this.PropertyA;
}
}
然后可以使用以下代码将此类列表转换为带有标题列的 CSV
string csvHeaderRow = String.Join(",", typeof(MyClass).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => x.Name).ToArray<string>()) + Environment.NewLine;
string csv= csvHeaderRow + String.Join(Environment.NewLine, MyClass.Select(x => x.ToString()).ToArray());
@Frank提供的链接中的代码从.NET通用列表创建CSV文件时,存在一个小问题,即以,
我修改代码的方式来结束每一行以摆脱它,希望它能对某人有所帮助。
/// <summary>
/// Creates the CSV from a generic list.
/// </summary>;
/// <typeparam name="T"></typeparam>;
/// <param name="list">The list.</param>;
/// <param name="csvNameWithExt">Name of CSV (w/ path) w/ file ext.</param>;
public static void CreateCSVFromGenericList<T>(List<T> list, string csvCompletePath)
{
if (list == null || list.Count == 0) return;
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
if (!Directory.Exists(Path.GetDirectoryName(csvCompletePath))) Directory.CreateDirectory(Path.GetDirectoryName(csvCompletePath));
if (!File.Exists(csvCompletePath)) File.Create(csvCompletePath);
using (var sw = new StreamWriter(csvCompletePath))
{
//make a new instance of the class name we figured out to get its props
object o = Activator.CreateInstance(t);
//gets all properties
PropertyInfo[] props = o.GetType().GetProperties();
//foreach of the properties in class above, write out properties
//this is the header row
sw.Write(string.Join(",", props.Select(d => d.Name).ToArray()) + newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
var row = string.Join(",", props.Select(d => item.GetType()
.GetProperty(d.Name)
.GetValue(item, null)
.ToString())
.ToArray());
sw.Write(row + newLine);
}
}
}
dev
存在,但文件不存在...我没有使用该权限吗?
sw.Write(string.Join(",", props.Select(d => d.Name).ToArray()) + newLine);
未经测试,但不知道您要实现的目标
我在这篇文章中对此进行了深入的解释。我将在代码中粘贴简短说明。
这是创建标题行的方法。它使用属性名称作为列名称。
private static void CreateHeader<T>(List<T> list, StreamWriter sw)
{
PropertyInfo[] properties = typeof(T).GetProperties();
for (int i = 0; i < properties.Length - 1; i++)
{
sw.Write(properties[i].Name + ",");
}
var lastProp = properties[properties.Length - 1].Name;
sw.Write(lastProp + sw.NewLine);
}
此方法创建所有值行
private static void CreateRows<T>(List<T> list, StreamWriter sw)
{
foreach (var item in list)
{
PropertyInfo[] properties = typeof(T).GetProperties();
for (int i = 0; i < properties.Length - 1; i++)
{
var prop = properties[i];
sw.Write(prop.GetValue(item) + ",");
}
var lastProp = properties[properties.Length - 1];
sw.Write(lastProp.GetValue(item) + sw.NewLine);
}
}
这是将它们组合在一起并创建实际文件的方法。
public static void CreateCSV<T>(List<T> list, string filePath)
{
using (StreamWriter sw = new StreamWriter(filePath))
{
CreateHeader(list, sw);
CreateRows(list, sw);
}
}
|
使用改进的版本生成定界文件。谢谢!
任何解决方案仅在列出列表(字符串)时有效
如果您有一个自己的对象的通用列表,例如list(of car),其中car具有n个属性,则必须循环每个car对象的PropertiesInfo。
查看:http : //www.csharptocsharp.com/generate-csv-from-generic-list
CsvHelper库在Nuget中非常流行。伙计,您值得! https://github.com/JoshClose/CsvHelper/wiki/Basics
使用CsvHelper确实很容易。默认设置是针对最常见的情况设置的。
这是一些设置数据。
Actors.csv:
Id,FirstName,LastName
1,Arnold,Schwarzenegger
2,Matt,Damon
3,Christian,Bale
Actor.cs(代表角色的自定义类对象):
public class Actor
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
使用CsvReader读取CSV文件:
var csv = new CsvReader( new StreamReader( "Actors.csv" ) );
var actorsList = csv.GetRecords();
写入CSV文件。
using (var csv = new CsvWriter( new StreamWriter( "Actors.csv" ) ))
{
csv.WriteRecords( actorsList );
}
无论出于何种原因,@ AliUmair都将编辑还原为他的答案,从而修复了无法按原样运行的代码,因此,这里是没有文件访问错误并且可以正确处理空对象属性值的工作版本:
/// <summary>
/// Creates the CSV from a generic list.
/// </summary>;
/// <typeparam name="T"></typeparam>;
/// <param name="list">The list.</param>;
/// <param name="csvNameWithExt">Name of CSV (w/ path) w/ file ext.</param>;
public static void CreateCSVFromGenericList<T>(List<T> list, string csvCompletePath)
{
if (list == null || list.Count == 0) return;
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
if (!Directory.Exists(Path.GetDirectoryName(csvCompletePath))) Directory.CreateDirectory(Path.GetDirectoryName(csvCompletePath));
using (var sw = new StreamWriter(csvCompletePath))
{
//make a new instance of the class name we figured out to get its props
object o = Activator.CreateInstance(t);
//gets all properties
PropertyInfo[] props = o.GetType().GetProperties();
//foreach of the properties in class above, write out properties
//this is the header row
sw.Write(string.Join(",", props.Select(d => d.Name).ToArray()) + newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
var row = string.Join(",", props.Select(d => $"\"{item.GetType().GetProperty(d.Name).GetValue(item, null)?.ToString()}\"")
.ToArray());
sw.Write(row + newLine);
}
}
}
http://cc.davelozinski.com/c-sharp/the-fastest-way-to-read-and-process-text-files
该网站对如何使用缓冲写入器进行文件写入进行了广泛的测试,逐行读取似乎是最好的方法,而使用字符串生成器是最慢的方法之一。
我大量使用他的技术来编写文件,使其工作良好。
通用的ToCsv()扩展方法:
用法示例:
"123".ToCsv() // "1,2,3"
"123".ToCsv(", ") // "1, 2, 3"
new List<int> { 1, 2, 3 }.ToCsv() // "1,2,3"
new List<Tuple<int, string>>
{
Tuple.Create(1, "One"),
Tuple.Create(2, "Two")
}
.ToCsv(t => t.Item2); // "One,Two"
((string)null).ToCsv() // throws exception
((string)null).ToCsvOpt() // ""
((string)null).ToCsvOpt(ReturnNullCsv.WhenNull) // null
实作
/// <summary>
/// Specifies when ToCsv() should return null. Refer to ToCsv() for IEnumerable[T]
/// </summary>
public enum ReturnNullCsv
{
/// <summary>
/// Return String.Empty when the input list is null or empty.
/// </summary>
Never,
/// <summary>
/// Return null only if input list is null. Return String.Empty if list is empty.
/// </summary>
WhenNull,
/// <summary>
/// Return null when the input list is null or empty
/// </summary>
WhenNullOrEmpty,
/// <summary>
/// Throw if the argument is null
/// </summary>
ThrowIfNull
}
/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsv<T>(
this IEnumerable<T> values,
string joinSeparator = ",")
{
return ToCsvOpt<T>(values, null /*selector*/, ReturnNullCsv.ThrowIfNull, joinSeparator);
}
/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="selector">An optional selector</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsv<T>(
this IEnumerable<T> values,
Func<T, string> selector,
string joinSeparator = ",")
{
return ToCsvOpt<T>(values, selector, ReturnNullCsv.ThrowIfNull, joinSeparator);
}
/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="returnNullCsv">Return mode (refer to enum ReturnNullCsv).</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsvOpt<T>(
this IEnumerable<T> values,
ReturnNullCsv returnNullCsv = ReturnNullCsv.Never,
string joinSeparator = ",")
{
return ToCsvOpt<T>(values, null /*selector*/, returnNullCsv, joinSeparator);
}
/// <summary>
/// Converts IEnumerable list of values to a comma separated string values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values">The values.</param>
/// <param name="selector">An optional selector</param>
/// <param name="returnNullCsv">Return mode (refer to enum ReturnNullCsv).</param>
/// <param name="joinSeparator"></param>
/// <returns>System.String.</returns>
public static string ToCsvOpt<T>(
this IEnumerable<T> values,
Func<T, string> selector,
ReturnNullCsv returnNullCsv = ReturnNullCsv.Never,
string joinSeparator = ",")
{
switch (returnNullCsv)
{
case ReturnNullCsv.Never:
if (!values.AnyOpt())
return string.Empty;
break;
case ReturnNullCsv.WhenNull:
if (values == null)
return null;
break;
case ReturnNullCsv.WhenNullOrEmpty:
if (!values.AnyOpt())
return null;
break;
case ReturnNullCsv.ThrowIfNull:
if (values == null)
throw new ArgumentOutOfRangeException("ToCsvOpt was passed a null value with ReturnNullCsv = ThrowIfNull.");
break;
default:
throw new ArgumentOutOfRangeException("returnNullCsv", returnNullCsv, "Out of range.");
}
if (selector == null)
{
if (typeof(T) == typeof(Int16) ||
typeof(T) == typeof(Int32) ||
typeof(T) == typeof(Int64))
{
selector = (v) => Convert.ToInt64(v).ToStringInvariant();
}
else if (typeof(T) == typeof(decimal))
{
selector = (v) => Convert.ToDecimal(v).ToStringInvariant();
}
else if (typeof(T) == typeof(float) ||
typeof(T) == typeof(double))
{
selector = (v) => Convert.ToDouble(v).ToString(CultureInfo.InvariantCulture);
}
else
{
selector = (v) => v.ToString();
}
}
return String.Join(joinSeparator, values.Select(v => selector(v)));
}
public static string ToStringInvariantOpt(this Decimal? d)
{
return d.HasValue ? d.Value.ToStringInvariant() : null;
}
public static string ToStringInvariant(this Decimal d)
{
return d.ToString(CultureInfo.InvariantCulture);
}
public static string ToStringInvariantOpt(this Int64? l)
{
return l.HasValue ? l.Value.ToStringInvariant() : null;
}
public static string ToStringInvariant(this Int64 l)
{
return l.ToString(CultureInfo.InvariantCulture);
}
public static string ToStringInvariantOpt(this Int32? i)
{
return i.HasValue ? i.Value.ToStringInvariant() : null;
}
public static string ToStringInvariant(this Int32 i)
{
return i.ToString(CultureInfo.InvariantCulture);
}
public static string ToStringInvariantOpt(this Int16? i)
{
return i.HasValue ? i.Value.ToStringInvariant() : null;
}
public static string ToStringInvariant(this Int16 i)
{
return i.ToString(CultureInfo.InvariantCulture);
}
这是我的扩展方法,为简单起见,它返回一个字符串,但我的实现将文件写入数据湖。
它提供任何定界符,在字符串中添加引号(如果它们包含定界符),并且交易将为空和空白。
/// <summary>
/// A class to hold extension methods for C# Lists
/// </summary>
public static class ListExtensions
{
/// <summary>
/// Convert a list of Type T to a CSV
/// </summary>
/// <typeparam name="T">The type of the object held in the list</typeparam>
/// <param name="items">The list of items to process</param>
/// <param name="delimiter">Specify the delimiter, default is ,</param>
/// <returns></returns>
public static string ToCsv<T>(this List<T> items, string delimiter = ",")
{
Type itemType = typeof(T);
var props = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);
var csv = new StringBuilder();
// Write Headers
csv.AppendLine(string.Join(delimiter, props.Select(p => p.Name)));
// Write Rows
foreach (var item in items)
{
// Write Fields
csv.AppendLine(string.Join(delimiter, props.Select(p => GetCsvFieldasedOnValue(p, item))));
}
return csv.ToString();
}
/// <summary>
/// Provide generic and specific handling of fields
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p"></param>
/// <param name="item"></param>
/// <returns></returns>
private static object GetCsvFieldasedOnValue<T>(PropertyInfo p, T item)
{
string value = "";
try
{
value = p.GetValue(item, null)?.ToString();
if (value == null) return "NULL"; // Deal with nulls
if (value.Trim().Length == 0) return ""; // Deal with spaces and blanks
// Guard strings with "s, they may contain the delimiter!
if (p.PropertyType == typeof(string))
{
value = string.Format("\"{0}\"", value);
}
}
catch (Exception ex)
{
throw ex;
}
return value;
}
}
用法:
// Tab Delimited (TSV)
var csv = MyList.ToCsv<MyClass>("\t");
List<int>
Select
除非缺少某些内容,否则框架3.5 中没有方法。