Convertendo uma lista genérica em uma string CSV

Eu tenho uma lista de valores inteiros (lista) e gostaria de gerar uma seqüência de valores delimitados por vírgula. Isso é todos os itens na lista de saída para uma única lista delimitada por vírgula.

Meus pensamentos … 1. passe a lista para um método. 2. Use stringbuilder para iterar a lista e acrescentar vírgulas. 3. Teste o último caractere e, se for uma vírgula, apague-o.

Quais são seus pensamentos? É este o melhor caminho?

Como meu código mudaria se eu quisesse lidar não apenas com números inteiros (meu plano atual), mas com seqüências de caracteres, longs, duplos, bools, etc, etc. no futuro? Acho que aceito uma lista de qualquer tipo.

É incrível o que o Framework já faz por nós.

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

Para o caso geral:

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

Como você pode ver, não é diferente. Cuidado, você pode precisar enrolar x.ToString() entre aspas (isto é, "\"" + x.ToString() + "\"" ) no caso de x.ToString() conter vírgulas.

Para uma leitura interessante sobre uma pequena variação disso: veja Comma Quabbling no blog de Eric Lippert.

Nota: Isto foi escrito antes do .NET 4.0 ser oficialmente lançado. Agora podemos apenas dizer

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

usando a sobrecarga String.Join(string, IEnumerable) . Este método irá projetar automaticamente cada elemento x para x.ToString() .

Você pode criar um método de extensão que você pode chamar em qualquer 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()); } 

Então você pode simplesmente chamar o método na lista original:

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

no 3.5, eu ainda era capaz de fazer isso. É muito mais simples e não precisa de lambda.

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

Você pode usar String.Join .

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

Se algum corpo quiser converter uma lista de objects de class customizada em vez de uma lista de string, então, sobrescreva o método ToString de sua class com a representação de linha csv de sua class.

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

Em seguida, o código a seguir pode ser usado para converter essa lista de classs em CSV com coluna de header

 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 o código no link dado pelo @Frank Crie um arquivo CSV a partir de uma lista genérica do .NET, houve um pequeno problema de terminar cada linha com um , eu modifiquei o código para me livrar dele.Espero que ajude alguém.

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

Qualquer solução só funciona se Listar uma lista (de string)

Se você tem uma lista genérica de seus próprios objects como lista (de carro) onde o carro tem n propriedades, você deve fazer um loop no PropertiesInfo de cada object de carro.

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

Eu gosto de um método de extensão simples e agradável

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

Então você pode simplesmente chamar o método na lista original:

 string CsvString = myList.ToCsv(); 

Mais limpo e mais fácil de ler do que algumas das outras sugestões.

Eu explico em profundidade neste post . Vou apenas colar o código aqui com breves descrições.

Aqui está o método que cria a linha de header. Ele usa os nomes das propriedades como nomes de colunas.

 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 cria todas as linhas de valor

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

E aqui está o método que os une e cria o arquivo real.

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

CsvHelper biblioteca é muito popular no Nuget.Você vale a pena, cara! https://github.com/JoshClose/CsvHelper/wiki/Basics

Usando CsvHelper é muito fácil. Suas configurações padrão são configuradas para os cenários mais comuns.

Aqui estão alguns dados de configuração.

Actors.csv:

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

Actor.cs (object de class personalizada que representa um ator):

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

Lendo o arquivo CSV usando o CsvReader:

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

var actorsList = csv.GetRecords ();

Escrevendo para um arquivo 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 site fez alguns testes extensivos sobre como escrever em um arquivo usando o gravador de buffer, lendo linha por linha parece ser a melhor maneira, usando construtor de seqüência de caracteres foi um dos mais lentos.

Eu uso suas técnicas muito para escrever coisas para o arquivo funciona bem.

O problema com String.Join é que você não está lidando com o caso de uma vírgula já existente no valor. Quando existe uma vírgula, você circunda o valor em Quotes e substitui todas as citações existentes por duplas.

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

Veja o módulo CSV

Um método de extensão ToCsv () de uso geral:

  • Suporta Int16 / 32/64, float, double, decimal e qualquer um que suporte ToString ()
  • Separador de junit personalizada opcional
  • Seletor personalizado opcional
  • Especificação de manipulação nula / vazia opcional (sobrecargas * Opt ())

Exemplos 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 

Implementação

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