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.
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()
.
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>());
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(", ");
Vous pouvez utiliser String.Join
.
String.Join(
",",
Array.ConvertAll(
list.ToArray(),
element => element.ToString()
)
);
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());
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);
}
}
}
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.
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
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 );
}
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);
}
}
Une méthode d'extension ToCsv () à usage général:
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);
}
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
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.