Programing

일반 목록을 CSV 문자열로 변환

lottogame 2020. 7. 7. 07:41
반응형

일반 목록을 CSV 문자열로 변환


정수 값 목록 (목록)이 있고 쉼표로 구분 된 값의 문자열을 생성하고 싶습니다. 이는 목록의 모든 항목이 단일 쉼표 제거 목록으로 출력됩니다.

내 생각은 ... 1. 목록을 메소드에 전달하십시오. 2. stringbuilder를 사용하여 목록을 반복하고 쉼표를 추가하십시오. 3. 마지막 문자를 테스트하고 쉼표 인 경우 삭제하십시오.

당신의 생각은 무엇입니까? 이것이 최선의 방법입니까?

앞으로 정수 (현재 계획)뿐만 아니라 문자열, long, double, bool 등을 처리하려면 코드가 어떻게 변경됩니까? 모든 유형의 목록을 허용한다고 생각합니다.


프레임 워크가 이미 우리를 위해하는 일이 놀랍습니다.

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

약간의 변형에 대한 흥미로운 내용 은 Eric Lippert의 블로그에서 Comma Quibbling참조하십시오 .

참고 : 이것은 .NET 4.0이 공식적으로 출시되기 전에 작성되었습니다. 이제 우리는 말할 수 있습니다

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

과부하 사용 String.Join<T>(string, IEnumerable<T>). 이 방법은 각 요소 x를에 자동으로 투사 합니다 x.ToString().


3.5에서는 여전히이 작업을 수행 할 수있었습니다. 훨씬 간단하고 람다가 필요하지 않습니다.

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

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(", ");

사용할 수 있습니다 String.Join.

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

본문 에서 문자열 목록 대신 사용자 정의 클래스 객체 목록을 변환하려는 경우 클래스의 csv 행 표현으로 클래스의 ToString 메소드를 대체하십시오.

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

@Frank가 제공 한 링크의 코드로 .NET Generic List에서 CSV 파일을 만들면, 코드를 수정하여 모든 줄을 끝내는 약간의 문제 가 발생했습니다.

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

        }
    }
}

모든 솔루션은 문자열 목록을 나열하는 경우에만 작동합니다.

car에 n 개의 속성이있는 list (of car)와 같은 고유 한 객체의 일반 목록이있는 경우 각 car 객체의 PropertiesInfo를 반복해야합니다.

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


나는 좋은 간단한 확장 방법을 좋아한다

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

그런 다음 원래 목록에서 메소드를 호출하면됩니다.

string CsvString = myList.ToCsv();

다른 제안보다 깨끗하고 읽기 쉽습니다.


게시물 에서 자세히 설명합니다 . 간단한 설명과 함께 여기에 코드를 붙여 넣습니다.

헤더 행을 만드는 방법은 다음과 같습니다. 속성 이름을 열 이름으로 사용합니다.

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

CsvHelper 라이브러리는 Nuget에서 매우 인기가 있습니다. https://github.com/JoshClose/CsvHelper/wiki/Basics

CsvHelper를 사용하는 것은 정말 쉽습니다. 기본 설정은 가장 일반적인 시나리오에 대해 설정됩니다.

다음은 약간의 설정 데이터입니다.

Actors.csv :

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

Actor.cs (액터를 나타내는 사용자 정의 클래스 객체) :

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

CsvReader를 사용하여 CSV 파일 읽기 :

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

var actorsList = csv.GetRecords ();

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

이 웹 사이트는 버퍼 작성기를 사용하여 파일에 쓰는 방법에 대한 광범위한 테스트를 수행했으며 한 줄씩 읽는 것이 가장 좋은 방법 인 것 같습니다. 문자열 작성기를 사용하는 것이 가장 느 렸습니다.

나는 그의 기술을 사용하여 파일을 작성하여 잘 작동시킵니다.


String.Join의 문제점은 값에 이미 존재하는 쉼표를 처리하지 않는다는 것입니다. 쉼표가 있으면 따옴표로 값을 묶고 기존의 모든 따옴표를 큰 따옴표로 바꿉니다.

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

CSV 모듈 참조


범용 ToCsv () 확장 메소드 :

  • Int16 / 32 / 64, float, double, decimal 및 ToString ()을 지원하는 모든 것을 지원합니다
  • 선택적 사용자 지정 조인 구분 기호
  • 선택적인 커스텀 셀렉터
  • 선택적 null / 빈 처리 사양 (* Opt () 오버로드)

사용 예 :

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

어떤 이유로 든 @AliUmair는 실행되지 않은 코드를 수정하는 답변으로 편집 내용을 되돌 렸습니다. 따라서 파일 액세스 오류가없고 null 객체 속성 값을 올바르게 처리하는 작업 버전이 있습니다.

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

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

        }
    }
}

참고 URL : https://stackoverflow.com/questions/1890093/converting-a-generic-list-to-a-csv-string

반응형