Как получить значения DataTable в строке с запятой, разделенной в c #? [Дубликат]

В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.

При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.

Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».

Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this. Возьмем этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте вашего кода:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference и otherReference оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.

88
задан Tushar Gupta 12 May 2014 в 11:47
поделиться

20 ответов

Следующая короткая версия открывается отлично в 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

И как сказал Тим, если вы находитесь на .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());

Как было предложено Кристианом, если вы хотите обрабатывать специальные символы, экранирующие в полях , замените блок цикла на:

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

И последнее предложение, вы можете написать содержимое csv по строкам, а не как целостный документ, чтобы избежать наличия большого документа в памяти.

182
ответ дан vc 74 19 August 2018 в 02:53
поделиться
  • 1
    +1: хорошая реализация linq – naveen 10 February 2011 в 17:34
  • 2
    Не нужно копировать ItemArray в новый String[], вы можете опустить .ToArray() с .NET 4 и использовать перегрузку String.Join , которая принимает IEnumerable<T> (отредактировано). – Tim Schmelter 5 June 2012 в 12:39
  • 3
    @TimSchmelter, да, но эти перегрузки были введены в .net4, код не будет компилироваться, если OP использует .net & lt; 4 – vc 74 22 June 2012 в 08:55
  • 4
    Этот метод не учитывает запятую внутри значения столбца. – Christian 2 November 2012 в 00:32
  • 5
    Вместо IEnumerable & lt; string & gt; fields = row.ItemArray.Select (field = & gt; field.ToString (). Заменить (& quot; \ & quot; & quot; \ & quot; \ & quot; & quot;)); sb.AppendLine (& quot; \ & quot; & quot; + string.Join (& quot; \ & quot;, \ ", & quot ;, поля) +" \ & quot; "); – Christian 2 November 2012 в 00:40

Если ваш код вызова ссылается на сборку 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) ваша сборка должна быть нацелена на платформу .NET 4.5, поскольку DataGridView не существует в 4.0 и 3). Метод будет терпеть неудачу, если буфер обмена используется другим процессом.

В любом случае, этот подход может быть неправильным для вашей ситуации, но он интересен тем не менее, и может быть еще один инструмент в вашем инструменте.

7
ответ дан Adam White 19 August 2018 в 02:53
поделиться
  • 1
    неортодоксальный и уникальный !!!! – Gary 24 June 2015 в 16:07
  • 2
    использование буфера обмена не требуется stackoverflow.com/questions/40726017/… . .GetClipboardContent также обрабатывает несколько краевых случаев значений, которые содержат ,. ", \t (он преобразует вкладку в пространство) – Slai 28 November 2016 в 14:43
  • 3
    Это хорошо, но что, если кто-то использует машину в одно и то же время и помещает что-то в буфер обмена в критический момент. – Ayo Adesina 22 February 2017 в 16:22

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

-1
ответ дан akram agbarya 19 August 2018 в 02:53
поделиться

Попробуйте поставить ; вместо ,

Надеемся, что это поможет

5
ответ дан alexl 19 August 2018 в 02:53
поделиться

Возможно, самым простым способом будет использование:

https://github.com/ukushu/DataExporter

, особенно в случае вашего данные datatable, содержащие символы /r/n или разделитель внутри ваших ячеек dataTable.

вам нужно только написать следующий код:

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();
1
ответ дан Andrew 19 August 2018 в 02:53
поделиться

Новая функция расширения, основанная на ответе Павла Гримшоу. Я очистил его и добавил способность обрабатывать неожиданные данные. (Пустые данные, встроенные кавычки и запятые в заголовках ...)

Он также возвращает строку, которая является более гибкой. Он возвращает 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();
9
ответ дан AnthonyVO 19 August 2018 в 02:53
поделиться
  • 1
    Здесь один из лучших. Отлично сработано. благодаря – Fandango68 23 April 2018 в 06:10

Я сделал это недавно, но включил двойные кавычки вокруг моих значений.

Например, измените эти две строки:

sb.Append("\"" + col.ColumnName + "\","); 
...
sb.Append("\"" + row[i].ToString() + "\","); 
6
ответ дан Ben Jakuben 19 August 2018 в 02:53
поделиться
  • 1
    Спасибо за предложение, но все они все еще находятся в первой ячейке каждой строки? – Darren Young 10 February 2011 в 17:32

Я завернул это в класс расширений, который позволяет вам называть:

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());
    }
}
20
ответ дан Camilo Terevinto 19 August 2018 в 02:53
поделиться

Прочитайте этот и этот ? Лучшей реализацией будет

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
ответ дан Community 19 August 2018 в 02:53
поделиться
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
ответ дан Ghebrehiywet 19 August 2018 в 02:53
поделиться

Чтобы имитировать 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
ответ дан James Carter 19 August 2018 в 02:53
поделиться

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
ответ дан Julia 19 August 2018 в 02:53
поделиться
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());
1
ответ дан Nam Nguyễn 19 August 2018 в 02:53
поделиться
  • 1
    Добро пожаловать в StackOverflow! Ответы лучше всего, когда они включают описание фрагмента кода. Я лично обнаружил, что, когда имена переменных совпадают между вопросом и ответом, они более полезны для меня. – AWinkle 24 May 2017 в 18:35

Попробуйте изменить 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());
6
ответ дан Neil Knight 19 August 2018 в 02:53
поделиться
  • 1
    Затем будут даны два возврата каретки. – Darren Young 10 February 2011 в 17:26
  • 2
    sb.AppendLine () работает тоже – alexl 10 February 2011 в 17:27
  • 3
    @alexl: Это то, с чем я изначально собирался, но это было не в моей голове, пока VS не загорелся: o) – Neil Knight 10 February 2011 в 17:29

Это усиление сообщения vc-74, которое обрабатывает запятые точно так же, как это делает Excel. 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;
    }
3
ответ дан Rhyous 19 August 2018 в 02:53
поделиться

Ошибка - разделитель списка.

Вместо записи sb.Append(something... + ',') вы должны поместить что-то вроде sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);

. Вы должны поместить символ разделителя списка в свою операционную систему (например, в примере выше) или разделитель списков на клиентском компьютере, где файл будет просматриваться. Другой вариант - настроить его в app.config или web.config в качестве параметра вашего приложения.

5
ответ дан Steve Czetty 19 August 2018 в 02:53
поделиться

Чтобы записать файл, я думаю, что следующий метод является наиболее эффективным и понятным: (вы можете добавить кавычки, если хотите)

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));
        }
    }
}
1
ответ дан Student222 19 August 2018 в 02:53
поделиться

В случае, если кто-то еще наткнулся на это, я использовал File.ReadAllText для получения CSV-данных, а затем я его модифицировал и написал с помощью File.WriteAllText. CRLF\r\n были хорошими, но вкладки \ t не учитывались при открытии Excel. (Все решения в этом потоке до сих пор используют разделитель запятой, но это не имеет значения.) Блокнот показал тот же формат в результирующем файле, что и в источнике. Diff даже показал файлы как идентичные. Но я понял, когда я открыл файл в Visual Studio с помощью двоичного редактора. Исходным файлом был Unicode, но целью был ASCII. Чтобы исправить, я изменил оба ReadAllText и WriteAllText с третьим аргументом, установленным как System.Text.Encoding.Unicode, и оттуда Excel смог открыть обновленный файл.

0
ответ дан TonyG 19 August 2018 в 02:53
поделиться

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
ответ дан user2023861 19 August 2018 в 02:53
поделиться
0
ответ дан cdiggins 30 October 2018 в 14:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: