Преобразование универсального списка в строку CSV-файла
У меня есть список целочисленных значений (список) и хотел бы создать строку с разделителями-запятыми значений. То есть все элементы в списке выводятся в один список с разделителями-запятыми.
мои мысли...
1. передайте список методу.
2. Использовать StringBuilder, чтобы перебирать список и добавить запятых
3. Проверить последний символ и если это запятая, удалите его.
о чем ты думаешь? Это лучший способ?
Как бы мой код изменился, если бы я хотел обрабатывать не только целые числа (мой текущий план), но строки, длинные, двойные, bools и т. д. в будущем? Я думаю, сделать это принять список любого типа.
13 ответов:
это удивительно, что рамки уже делает для нас.
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()содержит запятые.для интересного чтения на небольшой вариант этого: см. Запятая Придирка в блоге Эрика Липперта.
Примечание: это было написано до .NET 4.0 был официально выпустить. Теперь мы можем просто сказать
IEnumerable<T> sequence; string csv = String.Join(",", sequence);использование перегрузки
String.Join<T>(string, IEnumerable<T>). Этот метод будет автоматически проецировать каждый элементxдоx.ToString().
вы можете создать метод расширения, который можно вызвать на любой 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(", ");
в 3.5, я все еще был в состоянии сделать это. Его гораздо проще и не нужно лямбда.
String.Join(",", myList.ToArray<string>());
можно использовать
String.Join.String.Join( ",", Array.ConvertAll( list.ToArray(), element => element.ToString() ) );
Если какой-либо орган хочет преобразовать список пользовательские объекты класса вместо список строку затем переопределите метод ToString вашего класса с представлением строки csv вашего класса.
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 создайте CSV-файл из общего списка .NET была небольшая проблема окончания каждой строки с
,Я изменил код, чтобы избавиться от него.Надеюсь, это кому-то поможет./// <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); } } }
любое решение работает только если список(строки)
Если у вас есть общий список ваших собственных объектов, таких как list(of car), где car имеет N свойств, вы должны зациклить PropertiesInfo каждого объекта car.
посмотрите на:http://www.csharptocsharp.com/generate-csv-from-generic-list
мне нравится, хороший простой метод расширения
public static string ToCsv(this List<string> itemList) { return string.Join(",", itemList); }тогда вы можете просто вызвать метод в исходном списке:
string CsvString = myList.ToCsv();чище и легче читать, чем некоторые другие предложения.
Я объясняю это подробно в этом post. Я просто вставлю код здесь с краткими описаниями.
вот метод, который создает строку заголовка. Он использует имена свойств в качестве имен столбцов.
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); } }
библиотека CsvHelper очень популярна в Nuget.Ты того стоишь, чувак! https://github.com/JoshClose/CsvHelper/wiki/Basics
использование CsvHelper очень легко. Это настройки по умолчанию устанавливаются для наиболее распространенных сценариев.
вот немного данных настройки.
актеры.csv:
Id,FirstName,LastName 1,Arnold,Schwarzenegger 2,Matt,Damon 3,Christian,Baleактер.cs (пользовательский объект класса, представляющий субъекта):
public class Actor { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }чтение CSV-файла с помощью CsvReader:
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 ); }
http://cc.davelozinski.com/c-sharp/the-fastest-way-to-read-and-process-text-files
этот сайт сделал некоторые обширные испытания о том, как писать в файл с помощью буферизованного писателя, чтение строка за строкой, кажется, лучший способ, используя строковый конструктор был одним из самых медленных.
Я использую его методы много для написания материала, чтобы файл хорошо работает.
проблема со строкой.Join заключается в том, что вы не обрабатываете случай запятой, уже существующей в значении. Когда существует запятая, вы окружаете значение в кавычках и заменяете все существующие кавычки двойными кавычками.
String.Join(",",{"this value has a , in it","This one doesn't", "This one , does"});посмотреть CSV модуль
метод расширения общего назначения ToCsv ():
- поддерживает типа INT16/32/64, поплавок, двойной, десятичных, и ничего вспомогательная ToString ()
- дополнительный пользовательский разделитель соединения
- дополнительный пользовательский селектор
- необязательная спецификация обработки null / empty (*Opt() overloads)
Примеры Использования:
"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); }
Comments