wake-up-neo.com

Konvertieren einer generischen Liste in eine CSV-Zeichenfolge

Ich habe eine Liste ganzzahliger Werte (Liste) und möchte eine durch Kommas getrennte Wertefolge erzeugen. Das sind alle Elemente in der Liste, die in einer durch Kommas getrennten Liste ausgegeben werden.

Meine Gedanken ... 1. Übergeben Sie die Liste an eine Methode . 2. Verwenden Sie den Stringbuilder, um die Liste zu durchlaufen und Kommas anzuhängen 3. Testen Sie das letzte Zeichen und wenn es ein Komma ist, löschen Sie es.

Was sind deine Gedanken? Ist das der beste Weg?

Wie würde sich mein Code ändern, wenn ich in Zukunft nicht nur mit ganzen Zahlen (meinem aktuellen Plan), sondern mit Strings, Longs, Doubles, Bools usw. umgehen wollte? Ich nehme an, dass es eine Liste jeglicher Art akzeptiert.

115
DenaliHardtail

Es ist erstaunlich, was das Framework bereits für uns tut.

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

Für den allgemeinen Fall:

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

Wie Sie sehen, ist es praktisch nicht anders. Beachten Sie, dass Sie x.ToString() möglicherweise in Anführungszeichen einschließen müssen (d. H. "\"" + x.ToString() + "\""), falls x.ToString() Kommas enthält.

Für eine interessante Lektüre lesen Sie eine kleine Variante davon: siehe Comma Quibbling in Eric Lipperts Blog.

Hinweis: Dies wurde geschrieben, bevor .NET 4.0 offiziell veröffentlicht wurde. Jetzt können wir nur sagen

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

verwenden der Überladung String.Join<T>(string, IEnumerable<T>) . Diese Methode projiziert jedes Element x automatisch nach x.ToString().

211
jason

in 3.5 konnte ich das noch tun. Es ist viel einfacher und braucht kein Lambda.

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

Sie können eine Erweiterungsmethode erstellen, die Sie für jedes IEnumerable aufrufen können:

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

Dann können Sie die Methode einfach in der ursprünglichen Liste aufrufen:

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

Sie können String.Join verwenden.

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

Wenn ein Body eine Liste von benutzerdefinierten Klassenobjekten anstelle von String-Liste konvertieren möchte, überschreiben Sie die ToString-Methode Ihrer Klasse mit der csv-Zeilendarstellung Ihrer Klasse.

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

Anschließend kann der folgende Code verwendet werden, um diese Klassenliste in CSV mitHeaderspalteumzuwandeln.

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

Da der Code in dem von @Frank angegebenen Link Erstellen einer CSV-Datei aus einer generischen .NET-Liste es gab ein kleines Problem, jede Zeile mit einem , zu beenden, änderte ich den Code, um ihn zu entfernen .

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

Ich mag eine Nizza einfache Erweiterungsmethode

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

Dann können Sie die Methode einfach in der ursprünglichen Liste aufrufen:

string CsvString = myList.ToCsv();

Sauberer und leichter zu lesen als andere Vorschläge.

3
Griffo

Jede Lösung funktioniert nur, wenn Sie eine Liste (von Zeichenfolge) auflisten.

Wenn Sie eine generische Liste Ihrer eigenen Objekte haben, z. B. Liste (von Auto), für die Auto n Eigenschaften hat, müssen Sie die PropertiesInfo jedes Autoobjekts durchlaufen.

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

3
Frank M.

Die CsvHelper-Bibliothek ist im Nuget sehr beliebt. Sie sind es wert, Mann! https://github.com/JoshClose/CsvHelper/wiki/Basics

Die Verwendung von CsvHelper ist wirklich einfach. Die Standardeinstellungen sind für die gängigsten Szenarien eingerichtet.

Hier sind ein paar Setup-Daten.

Actors.csv:

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

Actor.cs (benutzerdefiniertes Klassenobjekt, das einen Schauspieler darstellt):

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

Lesen der CSV-Datei mit CsvReader:

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

var actsList = csv.GetRecords ();

Schreiben in eine CSV-Datei.

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

Ich erkläre es ausführlich in diesem post . Ich füge einfach den Code mit kurzen Beschreibungen hier ein.

Hier ist die Methode, mit der die Kopfzeile erstellt wird. Es verwendet die Eigenschaftsnamen als Spaltennamen.

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

Diese Methode erstellt alle Wertzeilen

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

Und hier ist die Methode, die sie zusammenbringt und die eigentliche Datei erstellt.

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

Eine ToCsv () - Erweiterungsmethode für allgemeine Zwecke: 

  • Unterstützt Int16/32/64, float, double, decimal und alles was ToString ()
  • Optionales benutzerdefiniertes Join-Trennzeichen
  • Optionale benutzerdefinierte Auswahl
  • Optionale Null-/Leerbehandlungsspezifikation (* Opt () - Überladungen)

Anwendungsbeispiele:

"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

Implementierung

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

Das Problem mit String.Join besteht darin, dass Sie den Fall eines bereits im Wert vorhandenen Kommas nicht behandeln. Wenn ein Komma existiert, umgeben Sie den Wert in Anführungszeichen und ersetzen Sie alle vorhandenen Anführungszeichen durch doppelte Anführungszeichen.

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

Siehe CSV-Modul

0
vbjay

http://cc.davelozinski.com/c-sharp/the-schnellste-weg-zur-lesen-und-Prozesstextdateien

Diese Website hat ausführlich getestet, wie mit gepuffertem Schreiber in eine Datei geschrieben wird. Das Lesen von Zeile für Zeile scheint der beste Weg zu sein. Die Verwendung von String Builder war eine der langsamsten. 

Ich benutze seine Techniken sehr oft, um Sachen zu schreiben, die gut funktionieren. 

0
Chong Ching