c # csv'ye verilebilir


113

Biri bana aşağıdaki kodun neden çalışmadığını söyleyebilir mi? Veriler csv dosyasına kaydedilir, ancak veriler ayrılmaz. Hepsi, her satırın ilk hücresinde bulunur.

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

Teşekkürler.



Yüksek performanslı Eklenti geliştirdim. bu yanıtı
Nigje

Yanıtlar:


229

Aşağıdaki daha kısa sürüm Excel'de iyi açılıyor, belki de sorununuz sondaki virgüldü

.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

Ve Tim'in işaret ettiği gibi, .net> = 4 üzerindeyseniz, daha da kısaltabilirsiniz:

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'ın önerdiği gibi, alanlardan kaçan özel karakterleri işlemek istiyorsanız, döngü bloğunu şu şekilde değiştirin:

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

Ve son öneri, bellekte büyük bir belge olmasını önlemek için csv içeriğini bütün bir belge yerine satır satır yazabilirsiniz.


2
Kopyalamak gerek yok ItemArrayyeni için String[], sen atlayabilirsiniz .ToArray().NET 4 ile ve kullanımı String.Joinaşırı bir sürer IEnumerable<T>(düzenlenmiş).
Tim Schmelter

2
@TimSchmelter, evet, ancak bu aşırı yüklemeler .net4'te tanıtıldı, OP .net kullanırsa kod derlenmez <4
vc 74

18
Bu yöntem, bir sütun değerinin içindeki virgül hesaba katılmaz.
Christian

2
Bunun yerine IEnumerable <string> fields = row.ItemArray.Select (field => field.ToString (). Replace ("\" "," \ "\" ")); sb.AppendLine (" \ "" + string.Join ("\", \ "", alanlar) + "\" ");
Christian

2
@ Si8 Ne demek istiyorsun? Bu yanıt yalnızca db bileşenlerini kullanır ve &nbsptipik HTML / XML belgelerinde görülür. Tablo &nbsp;açıkça içermediği sürece onu üreten yukarıdaki kod değildir
vc 74

37

Bunu aşağıdakileri aramanıza izin veren bir uzantı sınıfına sardım:

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

herhangi bir DataTable'da.

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

25

Paul Grimshaw'ın cevabına dayanan yeni bir uzantı işlevi. Temizledim ve beklenmedik verileri işleme yeteneği ekledim. (Boş Veriler, Gömülü Alıntılar ve başlıklarda virgül var ...)

Ayrıca daha esnek olan bir dize döndürür. Tablo nesnesi herhangi bir yapı içermiyorsa Null döndürür.

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

Bunu şu şekilde adlandırırsınız:

var csvData = dataTableOject.ToCsv();

1
Bu, burada kalanların en iyisi. Aferin. Teşekkürler
Fandango68

Harika çözüm. Yerel olarak yorumlar eklendi, ancak dağa tırmanmaya gerek kalmadan kutunun dışında kullanabildi. Teşekkür ederim.
j.hull

Bunu sevdim! Statik olmayan bir yöntem olarak kullandım ve DataTable'ımı bir parametre olarak geçtim. Harika çalıştı, teşekkürler.
Kid Koder

9

Çağıran kodunuz System.Windows.Formsmontajı referans veriyorsa, tamamen farklı bir yaklaşım düşünebilirsiniz. Stratejim, bunu çok az kod satırında ve sütunlar ve satırlar arasında döngü yapmak zorunda kalmadan gerçekleştirmek için çerçeve tarafından zaten sağlanan işlevleri kullanmaktır. Aşağıdaki kodun yaptığı şey, programlı DataGridViewolarak anında bir tane oluşturmak DataGridView.DataSourceve DataTable. Sonra, ben programlı içinde (başlık dahil) tüm hücreleri seçin DataGridViewve çağrı DataGridView.GetClipboardContent()Windows'un içine sonuçları yerleştirerek, Clipboard. Ardından, panonun içeriğini bir çağrıya File.WriteAllText()'yapıştırıyorum' ve 'yapıştır'ın biçimlendirmesini olarak belirlediğinizden emin olun TextDataFormat.CommaSeparatedValue.

İşte kod:

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

Ayrıca, başlamadan önce panonun içeriğini koruduğumdan ve işim bittikten sonra da geri yüklediğimden emin olurum, böylece kullanıcı bir dahaki sefere yapıştırmaya çalıştığında beklenmedik çöpler alamaz. Bu yaklaşımla ilgili ana uyarılar 1) Sınıfınızın referans vermesi gerekir System.Windows.Forms, ki bu bir veri soyutlama katmanında geçerli olmayabilir, 2) DataGridView 4.0'da olmadığı için derlemeniz .NET 4.5 çerçevesi için hedeflenmelidir, ve 3) Pano başka bir işlem tarafından kullanılıyorsa yöntem başarısız olur.

Her neyse, bu yaklaşım sizin durumunuz için doğru olmayabilir, ancak yine de ilginç değildir ve alet kutunuzdaki başka bir araç olabilir.


1
Pano kullanmak gerekli değildir stackoverflow.com/questions/40726017/… . .GetClipboardContentayrıca, içeren değerlerin birkaç uç durumunu da işler ,. ", \t(sekmeyi boşluğa dönüştürür)
Slai

2
Bu iyidir, ancak ya birisi aynı anda makineyi kullanıyorsa ve kritik anda Panoya bir şey koyarsa.
Ayo Adesina

7

Bunu yakın zamanda yaptım ama değerlerimin etrafına çift tırnak ekledim.

Örneğin, şu iki satırı değiştirin:

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

Öneriniz için teşekkürler, ancak tüm veriler hala her satırın ilk hücresinde mi?
Darren Young

7

Değiştirmeyi deneyin sb.Append(Environment.NewLine);için 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());

Bu daha sonra iki carraige dönüşü verecektir.
Darren Young

@alexl: Başlangıçta benim yaptığım buydu, ama VS ateşlenene kadar aklımın ucundaydı: o)
Neil Knight

5

Koymayı dene ; yerine,

Umarım yardımcı olur


5

Oku Bu ve bu ?


Daha iyi bir uygulama olacaktır

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

Hata, liste ayırıcıdır.

Yazmak yerine şöyle bir sb.Append(something... + ',')şey koymalısınsb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);

İşletim sisteminizde yapılandırılan liste ayırıcı karakterini (yukarıdaki örnekte olduğu gibi) veya liste ayırıcıyı dosyanın izleneceği istemci makinesine koymalısınız. Diğer bir seçenek de bunu app.config veya web.config içinde uygulamanızın bir parammetresi olarak yapılandırmak olabilir.


5

4 satır kod:

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

Not ToList()sonunda önemli; İfade değerlendirmesini zorlamak için bir şeye ihtiyacım var. Kod golfü yapsaydım, kullanabilirdimMin() onun yerine .

Ayrıca, son çağrı nedeniyle sonucun sonunda yeni bir satır olacağını unutmayın AppendLine(). Bunu istemeyebilirsin. TrimEnd()Kaldırmak için aramanız yeterlidir .


3

İşte virgülle Excel'in yaptığı gibi işleyen vc-74'ün gönderisinde bir geliştirme. Veride virgül varsa Excel verinin başına ve sonuna tırnak koyar, ancak verilerin virgül içermemesi durumunda alıntı yapmaz.

    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

Bir dosyaya yazmak için aşağıdaki yöntemin en verimli ve anlaşılır yöntem olduğunu düşünüyorum: (İsterseniz tırnak ekleyebilirsiniz)

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'yi taklit etmek için:

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'a hoş geldiniz! Yanıtlar, kod parçacığının bir açıklamasını içerdiklerinde en iyisidir. Kişisel olarak, değişken isimlerin soru ve cevap arasında sıralandığında bana daha çok yardımcı olduğunu buldum.
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

Muhtemelen en kolay yol kullanmak olacaktır:

https://github.com/ukushu/DataExporter

özellikle /r/ndataTable hücrelerinizin içinde karakterler veya ayırıcı sembol içeren datatable verileriniz olması durumunda . Hemen hemen tüm diğer cevaplar bu tür hücrelerle çalışmayacaktır.

tek ihtiyacınız olan aşağıdaki kodu yazmaktır:

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

Bu konuda dava başkasının tökezlemeleri, ben kullanıyordum File.ReadAllText CSV veri almak için ve sonra onu modifiye ve geri ile yazdım File.WriteAllText . \ R \ n CRLF'ler iyiydi, ancak Excel onu açtığında sekmeler göz ardı edildi. (Bu iş parçacığındaki tüm çözümler şu ana kadar bir virgül sınırlayıcı kullanıyor, ancak bu önemli değil.) Not Defteri, sonuçtaki dosyada kaynakla aynı biçimi gösterdi. Bir Diff, dosyaların aynı olduğunu bile gösterdi. Ancak dosyayı Visual Studio'da ikili düzenleyici ile açtığımda bir ipucu aldım. Kaynak dosya Unicode'du ancak hedef ASCII idi . Düzeltmek için, hem ReadAllText hem de WriteAllText'i System.Text.Encoding.Unicode olarak ayarlanmış üçüncü bağımsız değişkenle değiştirdim ve buradan Excel güncellenmiş dosyayı açabildi.


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

İşte Paul Grimshaw ve Anthony VO'nun önceki yanıtlarına dayanan çözümüm . Kodu Github'da bir C # projesinde gönderdim .

Ana katkım, açıkça bir a oluşturmayı ve manipüle etmeyi ortadan kaldırmak StringBuilderve bunun yerine yalnızcaIEnumerable . Bu, bellekte büyük bir tamponun tahsis edilmesini önler.

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

}

Bu yaklaşım, burada sorulduğu gibiIEnumerable DataTable'a dönüştürme ile güzel bir şekilde birleşir .


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

Ayrıca bazı verileri geçersiz kılmak zorunda kaldım, bu yüzden birkaç "eğer" ifadesi var. Bir alanın "N / A" yazmak için boş olması veya bir Tarih alanı "01/01/1900: 00" olarak biçimlendirilmişse "01/01/1900" olarak kaydedileceğinden emin olmam gerekiyordu yerine.


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

tüm veriler hala ilk hücrede ise bu, dosyayı açtığınız uygulamanın başka bir sınırlayıcı beklediği anlamına gelir. MSExcel, aksi belirtilmedikçe virgülü ayırıcı olarak işleyebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.