2009-12-11 5 views
108

У меня есть список целых значений (List) и вы хотите создать строку значений с разделителями-запятыми. Это все элементы в списке выводятся в один список с разделителями-запятыми.Преобразование общего списка в строку CSV

Мои мысли ... 1. Передайте список методу. 2. Используйте stringbuilder для перебора списка и добавления запятых 3. Протестируйте последний символ и, если это запятая, удалите его.

Что вы думаете? Это лучший способ?

Как бы изменился мой код, если бы я хотел обрабатывать не только целые числа (мой текущий план), но строки, длинные, двойные, bools и т. Д. И т. Д. В будущем? Я предполагаю, что он принимает список любого типа.

ответ

189

Удивительно, что Framework уже делает для нас.

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

В общем случае:

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

Как вы можете видеть, это не эффективно не отличается. Опасайтесь, что вам может понадобиться на самом деле обернуть x.ToString() в кавычки (т. Е. "\"" + x.ToString() + "\"") в случае, если x.ToString() содержит запятые.

Для интересного ознакомьтесь с небольшим вариантом этого: см. Comma Quibbling в блоге Эрика Липперта.

Примечание: Это было написано до того, как был выпущен .NET 4.0. Теперь мы можем просто сказать

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

используя перегрузку String.Join<T>(string, IEnumerable<T>). Этот метод автоматически проецирует каждый элемент x на x.ToString().

+0

'' Список не имеет метод 'Select' в рамках 3.5 если я не хватает чего-то. – ajeh

+2

@ajeh: Вам, вероятно, не хватает инструкции 'using'. – jason

+0

Какой конкретный импорт? – ajeh

7

Вы можете использовать String.Join.

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

Не нужно указывать параметры типового типа при вызове 'ConvertAll' здесь - как' int', так и 'string' будут выведены. –

+0

Спасибо за подсказку. –

+1

Вместо того, чтобы делать 'Array.ConvertAll (... ', вы можете просто выполнить команду list.ConvertAll (e => e.ToString()). ToArray)', просто меньше набрав. – David

10

Вы можете создать метод расширения, который вы можете позвонить по любому 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()); 
} 

Тогда вы можете просто вызвать метод в первоначальном списке:

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

в 3,5, я все еще мог это сделать. Это намного проще и не нуждается в лямбда.

String.Join(",", myList.ToArray<string>()); 
+0

'ToArray()' метод 'List ' нельзя использовать с аргументом типа в фреймворке 3.5, если мне не хватает чего-то. – ajeh

3

Любая работа решения, только если список списка (строки)

Если у вас есть общий список ваших собственных объектов, как список (автомобиль), где автомобиль имеет п свойство, вы должны мертвая недвижимость каждого автомобиля.

Посмотрите: http://www.csharptocsharp.com/generate-csv-from-generic-list

+1

Вы не можете переопределить ToString класса и использовать методы выше? –

5

Если какое-либо тело хочет, чтобы преобразовать список пользовательского класса объектов вместо список строки затем переопределить метод ToString вашего класса с CSV строки представления вашего класса.

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

Тогда следующий код может быть использован для преобразования этого списка класса в формате CSV с заголовке столбца

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

Поскольку код в ссылке, заданной @Frank Create a CSV File from a .NET Generic List была небольшая проблема окончания каждой строки с , Я изменил код, чтобы избавиться от него. Надеюсь, это поможет кому-то.

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

     } 
    } 
} 
+0

Дополнительная информация: Процесс не может получить доступ к файлу 'c: \ temp \ matchingMainWav.csv', потому что он используется другим процессом. папка 'dev' существует, но не файл ... я не использую это право? –

+0

Метод File.Create создает файл и открывает FileStream в файле. Таким образом, ваш файл уже открыт. Вам действительно не нужен файл. Создайте метод вообще: – David

3

я как хороший простой, метод расширения

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

Тогда вы можете просто вызвать метод в первоначальном списке:

string CsvString = myList.ToCsv(); 

уборщик и легче читать, чем некоторые другие предложения.

0

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

Этот сайт сделал обширное исследование о том, как записать в файл с использованием забуференной писателя, построчно чтение, кажется, лучший способ, с помощью строки строитель был один из самых медленных.

Я использую его методы для написания материала, чтобы он работал хорошо.

0

Проблема с String.Join заключается в том, что вы не обрабатываете случай с запятой, уже существующей в значении. Когда существует запятая, вы окружаете значение в Quotes и заменяете все существующие Quotes двойными кавычками.

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

См CSV Module

2

я объяснить углубленные в этом post. Я просто вставляю код здесь с краткими описаниями.

Вот метод, который создает строку заголовка. Он использует имена свойств как имена столбцов.

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

Этот метод создает все значение строки

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

А вот метод, который объединяет их и создает фактический файл.

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

Это работает очень хорошо. Я улучшил это, чтобы передать разделитель в качестве параметра, поэтому можно создать любой тип разделительного файла. CSVs больно иметь дело, если текст содержит запятые, поэтому я генерирую файлы '|' с разделителями с использованием улучшенной версии. Благодаря! – Shiva

1

Библиотека CsvHelper очень популярна в Nuget. Вы стоите того, мужчина! https://github.com/JoshClose/CsvHelper/wiki/Basics

Использование CsvHelper очень просто. Настройки по умолчанию настроены для наиболее распространенных сценариев.

Вот несколько данных настройки.

Actors.csv:

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

Актер.CS (пользовательский объект класса, который представляет актер):

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

Чтение файла CSV с помощью CSVReader:

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

уага actorsList = csv.GetRecords();

Запись в файл CSV.

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

общего назначения ToCsv() метод расширения:

  • Поддерживает Int16/32/64, поплавок, двойной, десятичной, и все, что поддерживающий ToString()
  • Дополнительный заказ присоединиться сепаратор
  • Дополнительный пользовательский селектор
  • Дополнительная спецификация null/empty handling (* Перегрузка() перегрузки)

Примеры использования:

"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 

Реализация

/// <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); 
} 
Смежные вопросы