Conversión de una lista genérica en una cadena CSV

Tengo una lista de valores enteros (Lista) y me gustaría generar una cadena de valores delimitados por comas. Es decir, todos los elementos de la lista salen a una única lista delimitada por comas.

Mis pensamientos … 1. pasar la lista a un método. 2. Utilice stringbuilder para iterar la lista y añada comas 3. Pruebe el último carácter y si es una coma, elimínelo.

¿Cuáles son tus pensamientos? Es esta la mejor manera?

¿Cómo cambiaría mi código si quisiera manejar no solo enteros (mi plan actual) sino cadenas, anhelos, dobles, bools, etc. en el futuro? Supongo que lo hacen aceptar una lista de cualquier tipo.

Es sorprendente lo que el Framework ya hace por nosotros.

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

Para el caso general:

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

Como puede ver, efectivamente no es diferente. Tenga en cuenta que es posible que necesite ajustar x.ToString() entre comillas (es decir, "\"" + x.ToString() + "\"" ) en caso de que x.ToString() contenga comas.

Para una lectura interesante sobre una pequeña variante de esto: vea Coma Qubbling en el blog de Eric Lippert.

Nota: Esto fue escrito antes .NET 4.0 fue lanzado oficialmente. Ahora solo podemos decir

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

utilizando la sobrecarga String.Join(string, IEnumerable) . Este método proyectará automáticamente cada elemento x a x.ToString() .

Puede crear un método de extensión al que pueda llamar en cualquier 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()); } 

Entonces puede simplemente llamar al método en la lista original:

 string commaSeparated = myList.JoinStrings(", "); 

en 3.5, todavía pude hacer esto. Es mucho más simple y no necesita lambda.

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

Puedes usar String.Join .

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

Si un cuerpo desea convertir una lista de objetos de clase personalizados en lugar de una lista de cadenas , anule el método ToString de su clase con una representación de fila csv de su clase.

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

A continuación, se puede usar el siguiente código para convertir esta lista de clase en CSV con columna de encabezado

 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()); 

Como el código en el enlace proporcionado por @Frank Crea un archivo CSV desde una lista genérica de .NET había un pequeño problema de terminar cada línea con a , modifiqué el código para deshacerme de él. Espero que ayude a alguien.

 ///  /// 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); } } } 

Cualquier solución funciona solo si Lista una lista (de cadena)

Si tiene una lista genérica de su propia lista de Objetos como (auto) donde el auto tiene n propiedades, debe recorrer el Info de Propiedades de cada objeto de auto.

Mira: http://www.csharptocsharp.com/generate-csv-from-generic-list

Me gusta un buen método de extensión simple

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

Entonces puede simplemente llamar al método en la lista original:

 string CsvString = myList.ToCsv(); 

Más limpio y fácil de leer que algunas de las otras sugerencias.

Lo explico en profundidad en esta publicación . Simplemente pegaré el código aquí con breves descripciones.

Aquí está el método que crea la fila del encabezado. Utiliza los nombres de propiedad como nombres de columna.

 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); } 

Este método crea todas las filas de valores

 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); } } 

Y aquí está el método que los reúne y crea el archivo real.

 public static void CreateCSV(List list, string filePath) { using (StreamWriter sw = new StreamWriter(filePath)) { CreateHeader(list, sw); CreateRows(list, sw); } } 

La biblioteca CsvHelper es muy popular en Nuget. ¡Tú lo vales, hombre! https://github.com/JoshClose/CsvHelper/wiki/Basics

Usar CsvHelper es realmente fácil. Su configuración predeterminada está configurada para los escenarios más comunes.

Aquí hay un poco de datos de configuración.

Actors.csv:

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

Actor.cs (objeto de clase personalizado que representa un actor):

 public class Actor { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } 

Leyendo el archivo CSV usando CsvReader:

 var csv = new CsvReader( new StreamReader( "Actors.csv" ) ); 

var actorsList = csv.GetRecords ();

Escribir en un archivo 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

Este sitio web realizó algunas pruebas exhaustivas sobre cómo escribir en un archivo utilizando escritor en búfer, la lectura línea por línea parece ser la mejor, ya que el generador de cadenas fue uno de los más lentos.

Utilizo sus técnicas mucho para escribir cosas para archivar, funciona bien.

El problema con String.Join es que no estás manejando el caso de una coma que ya existe en el valor. Cuando existe una coma, rodeas el valor en Citas y reemplazas todas las Citas existentes con comillas dobles.

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

Ver módulo CSV

Un método de extensión ToCsv () de propósito general:

  • Admite Int16 / 32/64, flotante, doble, decimal y cualquier cosa que soporte ToString ()
  • Separador de unión personalizada opcional
  • Selector personalizado opcional
  • Especificación de manejo nulo / vacío opcional (* sobrecargas de Opt ())

Ejemplos de uso:

 "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 

Implementación

 ///  /// 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); }