csv에 c # 데이터 테이블


113

누군가 다음 코드가 작동하지 않는 이유를 알려주십시오. 데이터는 csv 파일에 저장되지만 데이터는 분리되지 않습니다. 그것은 모두 각 행의 첫 번째 셀 내에 존재합니다.

StringBuilder sb = new StringBuilder();

foreach (DataColumn col in dt.Columns)
{
    sb.Append(col.ColumnName + ',');
}

sb.Remove(sb.Length - 1, 1);
sb.Append(Environment.NewLine);

foreach (DataRow row in dt.Rows)
{
    for (int i = 0; i < dt.Columns.Count; i++)
    {
        sb.Append(row[i].ToString() + ",");
    }

    sb.Append(Environment.NewLine);
}

File.WriteAllText("test.csv", sb.ToString());

감사.



고성능 Extention을 개발했습니다. 확인 이 답변
Nigje

답변:


229

다음의 짧은 버전은 Excel에서 잘 열립니다. 문제는 후행 쉼표 일 수 있습니다.

.net = 3.5

StringBuilder sb = new StringBuilder(); 

string[] columnNames = dt.Columns.Cast<DataColumn>().
                                  Select(column => column.ColumnName).
                                  ToArray();
sb.AppendLine(string.Join(",", columnNames));

foreach (DataRow row in dt.Rows)
{
    string[] fields = row.ItemArray.Select(field => field.ToString()).
                                    ToArray();
    sb.AppendLine(string.Join(",", fields));
}

File.WriteAllText("test.csv", sb.ToString());

.net> = 4.0

Tim이 지적했듯이 .net> = 4에 있으면 더 짧게 만들 수 있습니다.

StringBuilder sb = new StringBuilder(); 

IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                  Select(column => column.ColumnName);
sb.AppendLine(string.Join(",", columnNames));

foreach (DataRow row in dt.Rows)
{
    IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
    sb.AppendLine(string.Join(",", fields));
}

File.WriteAllText("test.csv", sb.ToString());

Christian이 제안한대로 필드에서 이스케이프되는 특수 문자를 처리하려면 루프 블록을 다음과 같이 바꾸십시오.

foreach (DataRow row in dt.Rows)
{
    IEnumerable<string> fields = row.ItemArray.Select(field => 
      string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
    sb.AppendLine(string.Join(",", fields));
}

마지막으로 csv 콘텐츠를 전체 문서가 아닌 한 줄씩 작성하여 메모리에 큰 문서가 생기지 않도록 할 수 있습니다.


2
ItemArray새 .NET 에 복사 할 필요가 없습니다. .NET 4 String[]에서는 생략 하고 (편집 됨) 오버로드.ToArray()사용할 수 있습니다 . String.JoinIEnumerable<T>
Tim Schmelter 2012-06-05

2
@TimSchmelter, 예,하지만 이러한 과부하는 .net4에 도입되었습니다. OP가 .net <4
vc를

18
이 방법은 열 값 내부의 쉼표를 고려하지 않습니다.
Christian

2
대신 IEnumerable <string> fields = row.ItemArray.Select (field => field.ToString (). Replace ( "\" ","\ "\" ")); sb.AppendLine ("\ ""+ string.Join ( "\", \ "", 필드) + "\" ");
Christian

2
@ Si8 무슨 뜻이야? 이 답변은 db 구성 요소 만 사용하며 &nbspHTML / XML 문서의 일반적인 내용입니다. 테이블에 &nbsp;명시 적으로 포함되지 않는 한 생성하는 것은 위의 코드가 아닙니다
vc 74

36

이것을 확장 클래스로 묶어 다음을 호출 할 수 있습니다.

myDataTable.WriteToCsvFile("C:\\MyDataTable.csv");

모든 DataTable에서.

public static class DataTableExtensions 
{
    public static void WriteToCsvFile(this DataTable dataTable, string filePath) 
    {
        StringBuilder fileContent = new StringBuilder();

        foreach (var col in dataTable.Columns) 
        {
            fileContent.Append(col.ToString() + ",");
        }

        fileContent.Replace(",", System.Environment.NewLine, fileContent.Length - 1, 1);

        foreach (DataRow dr in dataTable.Rows) 
        {
            foreach (var column in dr.ItemArray) 
            {
                fileContent.Append("\"" + column.ToString() + "\",");
            }

            fileContent.Replace(",", System.Environment.NewLine, fileContent.Length - 1, 1);
        }

        System.IO.File.WriteAllText(filePath, fileContent.ToString());
    }
}

24

Paul Grimshaw의 답변을 기반으로 한 새로운 확장 기능. 나는 그것을 정리하고 예기치 않은 데이터를 처리하는 기능을 추가했습니다. (헤딩에 빈 데이터, 포함 된 따옴표 및 쉼표 ...)

또한 더 유연한 문자열을 반환합니다. 테이블 개체에 구조가 포함되어 있지 않으면 Null을 반환합니다.

    public static string ToCsv(this DataTable dataTable) {
        StringBuilder sbData = new StringBuilder();

        // Only return Null if there is no structure.
        if (dataTable.Columns.Count == 0)
            return null;

        foreach (var col in dataTable.Columns) {
            if (col == null)
                sbData.Append(",");
            else
                sbData.Append("\"" + col.ToString().Replace("\"", "\"\"") + "\",");
        }

        sbData.Replace(",", System.Environment.NewLine, sbData.Length - 1, 1);

        foreach (DataRow dr in dataTable.Rows) {
            foreach (var column in dr.ItemArray) {
                if (column == null)
                    sbData.Append(",");
                else
                    sbData.Append("\"" + column.ToString().Replace("\"", "\"\"") + "\",");
            }
            sbData.Replace(",", System.Environment.NewLine, sbData.Length - 1, 1);
        }

        return sbData.ToString();
    }

다음과 같이 호출합니다.

var csvData = dataTableOject.ToCsv();

1
이것은 여기 나머지 중에서 최고입니다. 잘 했어. 감사합니다
Fandango68

환상적인 솔루션. 로컬에서 댓글을 추가했지만 산을 오르지 않고도 즉시 사용할 수있었습니다. 감사합니다.
j.hull

이것을 좋아했습니다! 비 정적 메서드로 사용하고 내 DataTable을 매개 변수로 전달했습니다. 수고하셨습니다. 감사합니다.
Kid Koder

9

호출 코드가 System.Windows.Forms어셈블리를 참조하는 경우 근본적으로 다른 접근 방식을 고려할 수 있습니다. 내 전략은 프레임 워크에서 이미 제공하는 함수를 사용하여 열과 행을 반복 할 필요없이 매우 적은 코드 줄로이를 수행하는 것입니다. 아래 코드가하는 일은 프로그래밍 방식으로 DataGridView즉석에서 DataGridView.DataSource를 만들고 DataTable. 다음으로, DataGridView및 호출의 모든 셀 (헤더 포함)을 프로그래밍 방식으로 선택 DataGridView.GetClipboardContent()하고 결과를 Windows에 배치합니다 Clipboard. 그런 다음 클립 보드의 내용을에 대한 호출에 File.WriteAllText()'붙여 넣기'하여 '붙여 넣기'의 형식을 TextDataFormat.CommaSeparatedValue.

다음은 코드입니다.

public static void DataTableToCSV(DataTable Table, string Filename)
{
    using(DataGridView dataGrid = new DataGridView())
    {
        // Save the current state of the clipboard so we can restore it after we are done
        IDataObject objectSave = Clipboard.GetDataObject();

        // Set the DataSource
        dataGrid.DataSource = Table;
        // Choose whether to write header. Use EnableWithoutHeaderText instead to omit header.
        dataGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
        // Select all the cells
        dataGrid.SelectAll();
        // Copy (set clipboard)
        Clipboard.SetDataObject(dataGrid.GetClipboardContent());
        // Paste (get the clipboard and serialize it to a file)
        File.WriteAllText(Filename,Clipboard.GetText(TextDataFormat.CommaSeparatedValue));              

        // Restore the current state of the clipboard so the effect is seamless
        if(objectSave != null) // If we try to set the Clipboard to an object that is null, it will throw...
        {
            Clipboard.SetDataObject(objectSave);
        }
    }
}

또한 시작하기 전에 클립 보드의 내용을 보존하고 완료되면 복원하여 다음에 사용자가 붙여 넣으려고 할 때 예상치 못한 쓰레기가 발생하지 않도록합니다. 이 접근 방식에 대한 주요주의 사항은 1) 클래스가를 참조 System.Windows.Forms해야한다는 것입니다. 데이터 추상화 계층에서는 그렇지 않을 수 있습니다. 2) DataGridView가 4.0에 존재하지 않기 때문에 어셈블리가 .NET 4.5 프레임 워크를 대상으로해야합니다. 3) 다른 프로세스에서 클립 보드를 사용중인 경우 메서드가 실패합니다.

어쨌든,이 접근 방식은 귀하의 상황에 적합하지 않을 수 있지만 그다지 흥미롭지 않으며 도구 상자의 또 다른 도구가 될 수 있습니다.


1
클립 보드를 사용할 필요가 없습니다 . stackoverflow.com/questions/40726017/… .GetClipboardContent또한 ,. ", \t(탭을 공백으로 변환)
Slai

2
이것은 좋지만 누군가가 동시에 기계를 사용하고 중요한 순간에 클립 보드에 무언가를 넣으면 어떨까요?
Ayo Adesina

7

나는 최근에 이것을했지만 내 가치 주위에 큰 따옴표를 포함했습니다.

예를 들어 다음 두 줄을 변경합니다.

sb.Append("\"" + col.ColumnName + "\","); 
...
sb.Append("\"" + row[i].ToString() + "\","); 

제안 해 주셔서 감사합니다.하지만 모든 데이터가 여전히 각 행의 첫 번째 셀에 있습니까?
Darren Young

7

변경 시도 sb.Append(Environment.NewLine);sb.AppendLine();.

StringBuilder sb = new StringBuilder();          
foreach (DataColumn col in dt.Columns)         
{             
    sb.Append(col.ColumnName + ',');         
}          

sb.Remove(sb.Length - 1, 1);         
sb.AppendLine();          

foreach (DataRow row in dt.Rows)         
{             
    for (int i = 0; i < dt.Columns.Count; i++)             
    {                 
        sb.Append(row[i].ToString() + ",");             
    }              

    sb.AppendLine();         
}          

File.WriteAllText("test.csv", sb.ToString());

그러면 두 번의 carraige가 반환됩니다.
Darren Young

@alexl : 그게 제가 원래 함께했던 것입니다.하지만 VS가 시작될 때까지 머리 꼭대기에서 벗어났습니다. : o)
Neil Knight


5

이것이것을 읽으십시오 ?


더 나은 구현은

var result = new StringBuilder();
for (int i = 0; i < table.Columns.Count; i++)
{
    result.Append(table.Columns[i].ColumnName);
    result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}

foreach (DataRow row in table.Rows)
{
    for (int i = 0; i < table.Columns.Count; i++)
    {
        result.Append(row[i].ToString());
        result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
    }
}
 File.WriteAllText("test.csv", result.ToString());

5

오류는 목록 구분 기호입니다.

글을 쓰는 대신 sb.Append(something... + ',')다음과 같은 것을 넣어야합니다.sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);

운영 체제에 구성된 목록 구분 문자 (위의 예에서와 같이) 또는 파일을 감시 할 클라이언트 시스템에 목록 구분 기호를 넣어야합니다. 또 다른 옵션은 app.config 또는 web.config에서 애플리케이션의 매개 변수로 구성하는 것입니다.


5

4 줄의 코드 :

public static string ToCSV(DataTable tbl)
{
    StringBuilder strb = new StringBuilder();

    //column headers
    strb.AppendLine(string.Join(",", tbl.Columns.Cast<DataColumn>()
        .Select(s => "\"" + s.ColumnName + "\"")));

    //rows
    tbl.AsEnumerable().Select(s => strb.AppendLine(
        string.Join(",", s.ItemArray.Select(
            i => "\"" + i.ToString() + "\"")))).ToList();

    return strb.ToString();
}

점을 유의 ToList()끝이 중요하다 식 평가를 강제 할 무언가가 필요합니다. 내가 코드 골프를한다면 Min()대신 사용할 수 있습니다 .

또한에 대한 마지막 호출로 인해 결과의 끝에 개행 문자가 있습니다 AppendLine(). 이것을 원하지 않을 수도 있습니다. 간단히 전화 TrimEnd()하여 제거 할 수 있습니다 .


3

다음은 Excel과 동일한 방식으로 쉼표를 처리하는 vc-74 게시물의 개선 사항입니다. Excel은 데이터에 쉼표가 있으면 데이터를 따옴표로 묶지 만 데이터에 쉼표가 없으면 인용하지 않습니다.

    public static string ToCsv(this DataTable inDataTable, bool inIncludeHeaders = true)
    {
        var builder = new StringBuilder();
        var columnNames = inDataTable.Columns.Cast<DataColumn>().Select(column => column.ColumnName);
        if (inIncludeHeaders)
            builder.AppendLine(string.Join(",", columnNames));
        foreach (DataRow row in inDataTable.Rows)
        {
            var fields = row.ItemArray.Select(field => field.ToString().WrapInQuotesIfContains(","));
            builder.AppendLine(string.Join(",", fields));
        }

        return builder.ToString();
    }

    public static string WrapInQuotesIfContains(this string inString, string inSearchString)
    {
        if (inString.Contains(inSearchString))
            return "\"" + inString+ "\"";
        return inString;
    }

2

파일에 쓰려면 다음 방법이 가장 효율적이고 간단하다고 생각합니다. (원하는 경우 따옴표를 추가 할 수 있습니다.)

public static void WriteCsv(DataTable dt, string path)
{
    using (var writer = new StreamWriter(path)) {
        writer.WriteLine(string.Join(",", dt.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName)));
        foreach (DataRow row in dt.Rows) {
            writer.WriteLine(string.Join(",", row.ItemArray));
        }
    }
}

2

Excel CSV를 모방하려면 :

public static string Convert(DataTable dt)
{
    StringBuilder sb = new StringBuilder();

    IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                        Select(column => column.ColumnName);
    sb.AppendLine(string.Join(",", columnNames));

    foreach (DataRow row in dt.Rows)
    {
        IEnumerable<string> fields = row.ItemArray.Select(field =>
        {
            string s = field.ToString().Replace("\"", "\"\"");
            if(s.Contains(','))
                s = string.Concat("\"", s, "\"");
            return s;
        });
        sb.AppendLine(string.Join(",", fields));
    }

    return sb.ToString().Trim();
}

1
StringBuilder sb = new StringBuilder();
        SaveFileDialog fileSave = new SaveFileDialog();
        IEnumerable<string> columnNames = tbCifSil.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(",", columnNames));

        foreach (DataRow row in tbCifSil.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field =>string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
            sb.AppendLine(string.Join(",", fields));
        }

        fileSave.ShowDialog();
        File.WriteAllText(fileSave.FileName, sb.ToString());

StackOverflow에 오신 것을 환영합니다! 답변은 코드 조각에 대한 설명을 포함 할 때 가장 좋습니다. 저는 개인적으로 질문과 답변 사이에 변수 이름이 정렬 될 때 더 도움이된다는 것을 발견했습니다.
AWinkle

1
public void ExpoetToCSV(DataTable dtDataTable, string strFilePath)
{

    StreamWriter sw = new StreamWriter(strFilePath, false);
    //headers   
    for (int i = 0; i < dtDataTable.Columns.Count; i++)
    {
        sw.Write(dtDataTable.Columns[i].ToString().Trim());
        if (i < dtDataTable.Columns.Count - 1)
        {
            sw.Write(",");
        }
    }
    sw.Write(sw.NewLine);
    foreach (DataRow dr in dtDataTable.Rows)
    {
        for (int i = 0; i < dtDataTable.Columns.Count; i++)
        {
            if (!Convert.IsDBNull(dr[i]))
            {
                string value = dr[i].ToString().Trim();
                if (value.Contains(','))
                {
                    value = String.Format("\"{0}\"", value);
                    sw.Write(value);
                }
                else
                {
                    sw.Write(dr[i].ToString().Trim());
                }
            }
            if (i < dtDataTable.Columns.Count - 1)
            {
                sw.Write(",");
            }
        }
        sw.Write(sw.NewLine);
    }
    sw.Close();
}

1

아마도 가장 쉬운 방법은 다음을 사용하는 것입니다.

https://github.com/ukushu/DataExporter

특히 /r/ndataTable 셀 내부에 문자 또는 구분 기호가 포함 된 데이터 테이블 데이터의 경우 . 거의 모든 다른 답변은 이러한 셀에서 작동하지 않습니다.

다음 코드를 작성하기 만하면됩니다.

Csv csv = new Csv("\t");//Needed delimiter 

var columnNames = dt.Columns.Cast<DataColumn>().
    Select(column => column.ColumnName).ToArray();

csv.AddRow(columnNames);

foreach (DataRow row in dt.Rows)
{
    var fields = row.ItemArray.Select(field => field.ToString()).ToArray;
    csv.AddRow(fields);   
}

csv.Save();

0

다른 사람이 이것을 우연히 발견하는 경우 File.ReadAllText 를 사용하여 CSV 데이터를 얻은 다음 수정하고 File.WriteAllText로 다시 썼습니다 . \ r \ n CRLF는 괜찮 았지만 \ t 탭은 Excel에서 열 때 무시되었습니다. (지금까지이 스레드의 모든 솔루션은 쉼표 구분 기호를 사용하지만 중요하지 않습니다.) 메모장은 결과 파일에서 소스와 동일한 형식을 보여줍니다. Diff는 파일이 동일하다는 것을 보여주었습니다. 하지만 바이너리 편집기로 Visual Studio에서 파일을 열었을 때 단서가 생겼습니다. 소스 파일은 유니 코드 였지만 대상은 ASCII 였습니다. 수정하기 위해 세 번째 인수를 System.Text.Encoding.Unicode로 설정하여 ReadAllText와 WriteAllText를 모두 수정 했으며 거기에서 Excel에서 업데이트 된 파일을 열 수있었습니다.


0

FYR

private string ExportDatatableToCSV(DataTable dtTable)
{
    StringBuilder sbldr = new StringBuilder();
    if (dtTable.Columns.Count != 0)
    {
        foreach (DataColumn col in dtTable.Columns)
        {
            sbldr.Append(col.ColumnName + ',');
        }
        sbldr.Append("\r\n");
        foreach (DataRow row in dtTable.Rows)
        {
            foreach (DataColumn column in dtTable.Columns)
            {
                sbldr.Append(row[column].ToString() + ',');
            }
            sbldr.Append("\r\n");
        }
    }
    return sbldr.ToString();
}

0

다음은 Paul GrimshawAnthony VO의 이전 답변을 기반으로 한 내 솔루션 입니다. Github의 C # 프로젝트에 코드를 제출했습니다. .

저의 주요 기여는 a를 명시 적으로 만들고 조작 StringBuilder하지 않고 대신 IEnumerable. 이렇게하면 메모리에 큰 버퍼가 할당되지 않습니다.

public static class Util
{
    public static string EscapeQuotes(this string self) {
        return self?.Replace("\"", "\"\"") ?? "";
    }

    public static string Surround(this string self, string before, string after) {
        return $"{before}{self}{after}";
    }

    public static string Quoted(this string self, string quotes = "\"") {
        return self.Surround(quotes, quotes);
    }

    public static string QuotedCSVFieldIfNecessary(this string self) {
        return (self == null) ? "" : self.Contains('"') ? self.Quoted() : self; 
    }

    public static string ToCsvField(this string self) {
        return self.EscapeQuotes().QuotedCSVFieldIfNecessary();
    }

    public static string ToCsvRow(this IEnumerable<string> self){
        return string.Join(",", self.Select(ToCsvField));
    }

    public static IEnumerable<string> ToCsvRows(this DataTable self) {          
        yield return self.Columns.OfType<object>().Select(c => c.ToString()).ToCsvRow();
        foreach (var dr in self.Rows.OfType<DataRow>())
            yield return dr.ItemArray.Select(item => item.ToString()).ToCsvRow();
    }

    public static void ToCsvFile(this DataTable self, string path) {
        File.WriteAllLines(path, self.ToCsvRows());
    }

}

이 접근 방식은 여기에서 요청한대로IEnumerable DataTable 변환 하는 것과 잘 결합 됩니다 .


0
        DataTable dt = yourData();
        StringBuilder csv = new StringBuilder();
        int dcCounter = 0;

        foreach (DataColumn dc in dt.Columns)
        {
            csv.Append(dc);
            if (dcCounter != dt.Columns.Count - 1)
            {
                csv.Append(",");
            }
            dcCounter++;
        }
        csv.AppendLine();

        int numOfDc = dt.Columns.Count;
        foreach (DataRow dr in dt.Rows)
        {
            int colIndex = 0;
            while (colIndex <= numOfDc - 1)
            {
                var colVal = dr[colIndex].ToString();
                if (colVal != null && colVal != "")
                {
                    DateTime isDateTime;
                    if (DateTime.TryParse(colVal, out isDateTime))
                    {
                        csv.Append(Convert.ToDateTime(colVal).ToShortDateString());
                    }
                    else
                    {
                        csv.Append(dr[colIndex]);
                    }
                }
                else
                {
                    csv.Append("N/A");
                }
                if (colIndex != numOfDc - 1)
                {
                    csv.Append(",");
                }
                colIndex++;
            }
            csv.AppendLine();

나는 또한 몇 가지 "if else"문이있는 이유 인 데이터를 재정의해야했습니다. 필드가 비어있는 경우 대신 "N / A"를 입력하거나 날짜 필드의 형식이 "01/01/1900 : 00"인 경우 "01/01/1900"으로 저장되는지 확인해야했습니다. 대신.


0
StringBuilder sb = new StringBuilder();

        foreach (DataColumn col in table.Columns)
        {
            sb.Append(col.ColumnName + ";");
        }

        foreach (DataRow row in table.Rows)
        {
            sb.AppendLine();
            foreach (DataColumn col in table.Columns)
            {
                sb.Append($@"{Convert.ToString(row[col])}" + ";");
            }
        }
        File.WriteAllText(path, sb.ToString());

-1

모든 데이터가 여전히 첫 번째 셀에 있으면 파일을 연 응용 프로그램에 다른 구분 기호가 필요함을 의미합니다. MSExcel은 별도로 지정하지 않는 한 쉼표를 구분 기호로 처리 할 수 ​​있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.