web-dev-qa-db-fra.com

Conversion d'une liste générique en chaîne CSV

J'ai une liste de valeurs entières (Liste) et j'aimerais générer une chaîne de valeurs délimitées par des virgules. Tous les éléments de la liste sont affichés dans une liste délimitée par des virgules.

Mes pensées ... 1. passer la liste à une méthode . 2. Utilisez stringbuilder pour parcourir la liste et ajouter des virgules 3. Testez le dernier caractère et si c'est une virgule, supprimez-le.

Quelles sont vos pensées? Est-ce le meilleur moyen?

Comment mon code changerait-il si je voulais gérer non seulement des entiers (mon plan actuel), mais également des chaînes, des longs, des doubles, des bools, etc. Je suppose que le faire accepter une liste de tout type.

115
DenaliHardtail

C'est incroyable ce que le cadre fait déjà pour nous.

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

Pour le cas général:

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

Comme vous pouvez le constater, la différence n’est en réalité pas différente. Attention, vous devrez peut-être réellement mettre x.ToString() entre guillemets (c.-à-d. "\"" + x.ToString() + "\"") dans le cas où x.ToString() contient des virgules.

Pour une lecture intéressante sur une légère variante: voir Comma Quibbling sur le blog de Eric Lippert.

Remarque: ceci a été écrit avant la publication officielle de .NET 4.0. Maintenant on peut juste dire

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

en utilisant la surcharge String.Join<T>(string, IEnumerable<T>) . Cette méthode projettera automatiquement chaque élément x sur x.ToString().

211
jason

en 3.5, j'étais encore capable de le faire. C'est beaucoup plus simple et n'a pas besoin de lambda.

String.Join(",", myList.ToArray<string>());
11
Krishna

Vous pouvez créer une méthode d'extension que vous pouvez appeler sur n'importe quel 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());
}

Ensuite, vous pouvez simplement appeler la méthode sur la liste d'origine:

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

Vous pouvez utiliser String.Join.

String.Join(
  ",",
  Array.ConvertAll(
     list.ToArray(),
     element => element.ToString()
  )
);
7
João Angelo

Si un corps souhaite convertir la liste de objets de classe personnalisés au lieu de liste de chaînes, remplacez la méthode ToString de votre classe par la représentation de ligne csv de votre classe.

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

Ensuite, le code suivant peut être utilisé pour convertir cette liste de classes en CSV avec header column

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());
6
Zain Ali

Comme le code dans le lien donné par @Frank Créer un fichier CSV à partir d'une liste générique .NET il y avait un petit problème qui consistait à mettre fin à chaque ligne avec un , J'ai modifié le code pour m'en débarrasser. .

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

        }
    }
}
5
Ali Umair

J'aime une méthode d'extension simple et agréable

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

Ensuite, vous pouvez simplement appeler la méthode sur la liste d'origine:

string CsvString = myList.ToCsv();

Plus propre et plus facile à lire que certaines des autres suggestions.

3
Griffo

Toute solution ne fonctionne que si Lister une liste (de chaîne)

Si vous avez une liste générique de vos propres objets, telle que liste (de voiture) où voiture a n propriétés, vous devez mettre en boucle les propriétés InfoInfo de chaque objet voiture.

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

3
Frank M.

La bibliothèque CsvHelper est très populaire dans Nuget.Vous le valez bien, mec! https://github.com/JoshClose/CsvHelper/wiki/Basics

Utiliser CsvHelper est vraiment facile. Ses paramètres par défaut sont configurés pour les scénarios les plus courants.

Voici un peu de données de configuration.

Acteurs.csv:

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

Actor.cs (objet de classe personnalisé représentant un acteur):

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

Lecture du fichier CSV avec CsvReader:

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

var ActeursList = csv.GetRecords ();

Écriture dans un fichier CSV.

using (var csv = new CsvWriter( new StreamWriter( "Actors.csv" ) )) 
{
    csv.WriteRecords( actorsList );
}
2
Farb

Je l'explique en profondeur dans ce post . Je vais simplement coller le code ici avec une brève description.

Voici la méthode qui crée la ligne d'en-tête. Il utilise les noms de propriété comme noms de colonne.

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

Cette méthode crée toutes les lignes de valeur

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

Et voici la méthode qui les rassemble et crée le fichier réel.

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

Une méthode d'extension ToCsv () à usage général: 

  • Prend en charge Int16/32/64, float, double, decimal et tout ce qui prend en charge ToString ()
  • Séparateur de jointure personnalisé facultatif
  • Sélecteur personnalisé en option
  • Spécification facultative de manipulation null/vide (* surcharges Opt ())

Exemples d'utilisation:

"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

La mise en oeuvre

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

Le problème avec String.Join est que vous ne gérez pas le cas d'une virgule existant déjà dans la valeur. Quand une virgule existe, vous entourez la valeur dans les devis et remplacez tous les devis existants par des doubles.

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

Voir Module CSV

0
vbjay

http://cc.davelozinski.com/c-sharp/the-fastest-way-to-read-and-process-text-files

Ce site Web a effectué des tests approfondis sur la manière d'écrire dans un fichier à l'aide d'un scripteur en mémoire tampon. La lecture ligne par ligne semble être la meilleure solution, l'utilisation du générateur de chaînes de caractères étant l'une des plus lentes. 

J'utilise beaucoup ses techniques pour écrire des choses, mais ça marche bien. 

0
Chong Ching