Преобразование общего списка в строку CSV

У меня есть список целых значений (List) и хотел бы сгенерировать строку значений с разделителями-запятыми. Это все элементы в списке выводятся в один список с разделителями-запятыми.

Мои мысли … 1. Передайте список методу. 2. Используйте stringbuilder для перебора списка и добавления запятых. 3. Протестируйте последний символ и, если это запятая, удалите его.

Что ты думаешь? Это лучший способ?

Как изменился бы мой код, если бы я хотел обрабатывать не только целые числа (мой текущий план), но строки, длинные, двойные, bools и т. Д. И т. Д. В будущем? Я предполагаю, что он принимает список любого типа.

Удивительно, что Framework уже делает для нас.

List myValues; string csv = String.Join(",", myValues.Select(x => x.ToString()).ToArray()); 

Для общего случая:

 IEnumerable myList; string csv = String.Join(",", myList.Select(x => x.ToString()).ToArray()); 

Как вы можете видеть, это никоим образом не отличается. Помните, что вам может понадобиться на самом деле обернуть x.ToString() в кавычки (т. "\"" + x.ToString() + "\"" ) в случае, если x.ToString() содержит запятые.

Для интересного прочитайте небольшой вариант этого: см. Comma Quibbling в блоге Эрика Липперта.

Примечание. Это было написано до того, как был выпущен .NET 4.0. Теперь мы можем просто сказать

 IEnumerable sequence; string csv = String.Join(",", sequence); 

используя перегрузку String.Join(string, IEnumerable) . Этот метод автоматически проецирует каждый элемент x на x.ToString() .

Вы можете создать метод расширения, который вы можете вызвать в любом IEnumerable:

 public static string JoinStrings( this IEnumerable 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, я все еще мог это сделать. Это намного проще и не нуждается в lambda.

 String.Join(",", myList.ToArray()); 

Вы можете использовать String.Join .

 String.Join( ",", Array.ConvertAll( list.ToArray(), element => element.ToString() ) ); 

Если какой-либо орган хочет преобразовать список пользовательских объектов classа вместо списка строк, переопределите метод ToString вашего classа с представлением строки csv вашего classа.

 Public Class MyClass{ public int Id{get;set;} public String PropertyA{get;set;} public override string ToString() { return this.Id+ "," + this.PropertyA; } } 

Затем следующий код может быть использован для преобразования этого списка classов в CSV с колонкой заголовка

 string csvHeaderRow = String.Join(",", typeof(MyClass).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => x.Name).ToArray()) + Environment.NewLine; string csv= csvHeaderRow + String.Join(Environment.NewLine, MyClass.Select(x => x.ToString()).ToArray()); 

Как код в ссылке, заданной @Frank Создание файла CSV из .NET Generic List, была небольшая проблема с окончанием каждой строки с помощью a , я изменил код, чтобы избавиться от него. Надеюсь, это поможет кому-то.

 ///  /// Creates the CSV from a generic list. /// ; /// ; /// The list.; /// Name of CSV (w/ path) w/ file ext.; public static void CreateCSVFromGenericList(List 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 list (string)

Если у вас есть общий список ваших собственных объектов, таких как список (на машине), где у автомобиля есть n свойств, вы должны зацикливать PropertiesInfo каждого объекта автомобиля.

Посмотрите: http://www.csharptocsharp.com/generate-csv-from-generic-list

Мне нравится хороший простой метод расширения

  public static string ToCsv(this List itemList) { return string.Join(",", itemList); } 

Затем вы можете просто вызвать метод в исходном списке:

 string CsvString = myList.ToCsv(); 

Чище и легче читать, чем некоторые другие предложения.

Я объясняю это подробно в этом посте . Я просто вставляю код здесь с краткими описаниями.

Вот метод, который создает строку заголовка. Он использует имена свойств как имена столбцов.

 private static void CreateHeader(List 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(List 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(List 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 очень просто. Настройки по умолчанию настроены для наиболее распространенных сценариев.

Вот несколько настроек данных.

Actors.csv:

 Id,FirstName,LastName 1,Arnold,Schwarzenegger 2,Matt,Damon 3,Christian,Bale 

Actor.cs (пользовательский объект classа, который представляет актера):

 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

На этом веб-сайте было проведено некоторое обширное тестирование о том, как писать в файл с использованием буферизованного писателя, чтение строки за строкой, по-видимому, является лучшим способом, использование строкового построителя было одним из самых медленных.

Я использую его методы для написания файлов, чтобы он работал хорошо.

Проблема с String.Join заключается в том, что вы не обрабатываете случай уже существующей в значении запятой. Когда существует запятая, вы окружаете значение в Quotes и заменяете все существующие Quotes двойными кавычками.

 String.Join(",",{"this value has a , in it","This one doesn't", "This one , does"}); 

См. Модуль CSV

Метод расширения ToCsv () общего назначения:

  • Поддерживает Int16 / 32/64, float, double, decimal и все, что поддерживает ToString ()
  • Дополнительный настраиваемый разделитель соединений
  • Дополнительный настраиваемый селектор
  • Необязательная спецификация null / empty обработки (перегрузки * Opt ())

Примеры использования:

 "123".ToCsv() // "1,2,3" "123".ToCsv(", ") // "1, 2, 3" new List { 1, 2, 3 }.ToCsv() // "1,2,3" new List> { 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 

Реализация

 ///  /// Specifies when ToCsv() should return null. Refer to ToCsv() for IEnumerable[T] ///  public enum ReturnNullCsv { ///  /// Return String.Empty when the input list is null or empty. ///  Never, ///  /// Return null only if input list is null. Return String.Empty if list is empty. ///  WhenNull, ///  /// Return null when the input list is null or empty ///  WhenNullOrEmpty, ///  /// Throw if the argument is null ///  ThrowIfNull } ///  /// Converts IEnumerable list of values to a comma separated string values. ///  ///  /// The values. ///  /// System.String. public static string ToCsv( this IEnumerable values, string joinSeparator = ",") { return ToCsvOpt(values, null /*selector*/, ReturnNullCsv.ThrowIfNull, joinSeparator); } ///  /// Converts IEnumerable list of values to a comma separated string values. ///  ///  /// The values. /// An optional selector ///  /// System.String. public static string ToCsv( this IEnumerable values, Func selector, string joinSeparator = ",") { return ToCsvOpt(values, selector, ReturnNullCsv.ThrowIfNull, joinSeparator); } ///  /// Converts IEnumerable list of values to a comma separated string values. ///  ///  /// The values. /// Return mode (refer to enum ReturnNullCsv). ///  /// System.String. public static string ToCsvOpt( this IEnumerable values, ReturnNullCsv returnNullCsv = ReturnNullCsv.Never, string joinSeparator = ",") { return ToCsvOpt(values, null /*selector*/, returnNullCsv, joinSeparator); } ///  /// Converts IEnumerable list of values to a comma separated string values. ///  ///  /// The values. /// An optional selector /// Return mode (refer to enum ReturnNullCsv). ///  /// System.String. public static string ToCsvOpt( this IEnumerable values, Func 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); } 
Interesting Posts
Давайте будем гением компьютера.