Gravando dados no arquivo CSV em C #


198

Estou tentando gravar em um csvarquivo linha por linha usando a linguagem C #. Aqui está a minha função

string first = reader[0].ToString();
string second=image.ToString();
string csv = string.Format("{0},{1}\n", first, second);
File.WriteAllText(filePath, csv);

Toda a função é executada dentro de um loop, e cada linha deve ser gravada no csvarquivo. No meu caso, a próxima linha substitui a linha existente e, no final, estou recebendo um único registro no arquivo csv, que é o último. Como posso escrever todas as linhas do csvarquivo?


Em vez disso, use um StringBuildere faça um salvar?
Johan

1
Se isto não é uma tarefa que você precisa para cumprir diária, eu recomendo usar LinqPad, que vem com uma função útil para gravar dados em um arquivo CSV:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco

3
Em uma nota lateral, verifique se seus valores csv estão codificados. Ou seja, se um deles contiver uma vírgula ou um caractere de fim de linha, poderá atrapalhar seu arquivo. Eu normalmente apenas uso uma lib de terceiros para coisas de CSV.
Matthijs Wessels

@MatthijsWessels Alguma sugestão de biblioteca?
Arash Motamedi

Respostas:


311

ATUALIZAR

Nos meus dias ingênuos, sugeri fazer isso manualmente (era uma solução simples para uma pergunta simples); no entanto, devido a isso se tornar cada vez mais popular, eu recomendaria o uso da biblioteca CsvHelper que faz todas as verificações de segurança etc.

O CSV é muito mais complicado do que o que a pergunta / resposta sugere.

Resposta original

Como você já tem um loop, considere fazer o seguinte:

//before your loop
    var csv = new StringBuilder();

//in your loop
    var first = reader[0].ToString();
    var second = image.ToString();
    //Suggestion made by KyleMit
    var newLine = string.Format("{0},{1}", first, second);
    csv.AppendLine(newLine);  

//after your loop
    File.WriteAllText(filePath, csv.ToString());

Ou algo nesse sentido. Meu raciocínio é: você não precisará gravar no arquivo para cada item, apenas abrirá o fluxo uma vez e depois gravará nele.

Você pode substituir

File.WriteAllText(filePath, csv.ToString());

com

File.AppendAllText(filePath, csv.ToString());

se você deseja manter as versões anteriores do csv no mesmo arquivo

C # 6

Se você estiver usando o c # 6.0, poderá fazer o seguinte

var newLine = $"{first},{second}"

EDITAR

Aqui está um link para uma pergunta que explica o que Environment.NewLinefaz.


1
@Rajat não, porque 2 é uma nova linha
Johan

4
Você também pode se livrar de {2}e Environment.NewLinee usar AppendLineem vez deAppend
KyleMit

37
E o que acontece quando seu conteúdo CSV possui uma vírgula que precisa ser escapada? Você precisa de aspas. E o que acontece quando uma cotação precisa escapar? A criação correta de arquivos CSV é muito mais complexa do que esta resposta implica.
MgSam

1
Eu tenho um "ExceptionType": "System.UnauthorizedAccessException",
Chit Khine

3
Eu tentei sua solução, mas para mim (cultura francesa) funciona melhor usando em System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatorvez de vírgula.
A.Pissicat

89

Eu recomendo que você siga a rota mais tediosa. Especialmente se o tamanho do seu arquivo for grande.

using(var w = new StreamWriter(path))
{
    for( /* your loop */)
    {
        var first = yourFnToGetFirst();
        var second = yourFnToGetSecond();
        var line = string.Format("{0},{1}", first, second);
        w.WriteLine(line);
        w.Flush();
    }
}

File.AppendAllText()abre um novo arquivo, grava o conteúdo e depois fecha o arquivo. Abrir arquivos é uma operação com muitos recursos, do que gravar dados em fluxo aberto. Abrir / fechar um arquivo dentro de um loop causará queda no desempenho.

A abordagem sugerida por Johan resolve esse problema armazenando toda a saída na memória e depois escrevendo-a uma vez. No entanto (no caso de arquivos grandes), o programa consumirá uma grande quantidade de RAM e até travará comOutOfMemoryException

Outra vantagem da minha solução é que você pode implementar a pausa \ retomando salvando a posição atual nos dados de entrada.

upd. Colocado usando no lugar certo


1
Você deve colocar seu loop for dentro da usinginstrução Caso contrário, você vai reabrir o arquivo o tempo todo repetidamente.
Oliver

2
Boa resposta. Resposta correta se você remover "{2}" da string de formato e substituir "imagem" por "segundo" na mesma linha. Além disso, não esqueça de fechar o escritor com w.Close (); w.Flush () não é necessário.
MovAX13h

6
Esta resposta ignora a necessidade de escapar caracteres.
MgSam

Podemos também acrescentar que ajuda a definir o escritor como este: new StreamWriter (caminho, falsa, Encoding.UTF8)
Charkins12

se você tem algumas centenas de milhões de linhas ... você deve liberar todas as linhas?
Ardianto Suhendar 31/03/19

30

A gravação manual de arquivos CSV pode ser difícil, pois seus dados podem conter vírgulas e novas linhas. Sugiro que você use uma biblioteca existente.

Esta pergunta menciona algumas opções.

Existem bibliotecas de leitores / gravadores de CSV em C #?


4
Esta é a única resposta razoavelmente correta aqui. É uma pena que as outras respostas ruins que tentam fazê-lo manualmente tenham muitos votos positivos.
MgSam

17

Eu uso uma solução de duas análises, pois é muito fácil manter

// Prepare the values
var allLines = (from trade in proposedTrades
                select new object[] 
                { 
                    trade.TradeType.ToString(), 
                    trade.AccountReference, 
                    trade.SecurityCodeType.ToString(), 
                    trade.SecurityCode, 
                    trade.ClientReference, 
                    trade.TradeCurrency, 
                    trade.AmountDenomination.ToString(), 
                    trade.Amount, 
                    trade.Units, 
                    trade.Percentage, 
                    trade.SettlementCurrency, 
                    trade.FOP, 
                    trade.ClientSettlementAccount, 
                    string.Format("\"{0}\"", trade.Notes),                             
                }).ToList();

// Build the file content
var csv = new StringBuilder();
allLines.ForEach(line => 
{
    csv.AppendLine(string.Join(",", line));            
});

File.WriteAllText(filePath, csv.ToString());

1
Você pode encontrar pressão de memória com essa abordagem ao criar arquivos grandes. Se você removesse o .ToList, o allLines seria um IEnumerbale <object []>. Você pode então selecionar isso em vez de "para cada um", ou seja, allines, Select (line => csv.AppendLine (string.Join (",", line))) que forneceria a IEnumerable <string>. Agora isso pode ser passado apenas para o arquivo, mas o método WriteAllLines. Agora, isso significa que tudo é preguiçoso e você não precisa colocar tudo na memória, mas ainda assim obtém a sintaxe com a qual está feliz.
RhysC

Estou usando seu método para gravar em um arquivo CSV. É ótimo! Minha última implementação exige que eu use em File.AppendAllLines(filePath, lines, Encoding.ASCII);vez de File.WriteAllText(filePath, csv.ToString()); Então, estou fazendo algo muito desajeitado. Depois de criar o csv usando StringBuilder, eu o converto em uma List <string> para obter um IEnumerable para a chamada a AppendAllLines, que não aceita csv.ToString () como parâmetro: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));você tem uma maneira melhor de fazer isso?
Patricia

12

Em vez de ligar todas as vezes, AppendAllText()você pode pensar em abrir o arquivo uma vez e depois escrever o conteúdo inteiro uma vez:

var file = @"C:\myOutput.csv";

using (var stream = File.CreateText(file))
{
    for (int i = 0; i < reader.Count(); i++)
    {
        string first = reader[i].ToString();
        string second = image.ToString();
        string csvRow = string.Format("{0},{1}", first, second);

        stream.WriteLine(csvRow);
    }
}

@aMazing: Desculpe, um erro de digitação. Agora está corrigido.
1276 Oliver

Esta resposta inclui que a extensão deve ser csv. Se for xls, todos os dados aparecerão em uma coluna ... csv, aparecerão corretamente em cada coluna.
gman

10

Basta usar AppendAllText:

File.AppendAllText(filePath, csv);

A única desvantagem do AppendAllText é que ele gera erro quando o arquivo não existe, portanto, isso deve ser verificado

Desculpe, momento loiro antes de ler a documentação . De qualquer forma, o método WriteAllText substitui tudo o que foi gravado anteriormente no arquivo, se o arquivo existir.

Observe que seu código atual não está usando novas linhas apropriadas; por exemplo, no Bloco de notas, você verá tudo isso como uma linha longa. Altere o código para isso para ter novas linhas apropriadas:

string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);

2
@Rajat não, isso escreverá o novo caractere de linha logo após os dados da imagem.
Shadow Wizard é Ear For You

7

Em vez de reinventar a roda, uma biblioteca poderia ser usada. CsvHelperé ótimo para criar e ler arquivos csv. Suas operações de leitura e gravação são baseadas em fluxo e, portanto, também suportam operações com uma grande quantidade de dados.


Você pode escrever seu csv da seguinte maneira.

using(var textWriter = new StreamWriter(@"C:\mypath\myfile.csv"))
{
    var writer = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
    writer.Configuration.Delimiter = ",";

    foreach (var item in list)
    {
        writer.WriteField( "a" );
        writer.WriteField( 2 );
        writer.WriteField( true );
        writer.NextRecord();
    }
}

Como a biblioteca está usando reflexão, ela pega qualquer tipo e a analisa diretamente.

public class CsvRow
{
    public string Column1 { get; set; }
    public bool Column2 { get; set; }

    public CsvRow(string column1, bool column2)
    {
        Column1 = column1;
        Column2 = column2;
    }
}

IEnumerable<CsvRow> rows = new [] {
    new CsvRow("value1", true),
    new CsvRow("value2", false)
};
using(var textWriter = new StreamWriter(@"C:\mypath\myfile.csv")
{
    var writer = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
    writer.Configuration.Delimiter = ",";
    writer.WriteRecords(rows);
}

value1, true

valor2, falso


Se você quiser ler mais sobre as configurações e possibilidades das bibliotecas, faça isso aqui .


Talvez você precise passar CultureInfo para StreamWriter para que isso funcione.
alif

6
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;

public partial class CS : System.Web.UI.Page
{
    protected void ExportCSV(object sender, EventArgs e)
    {
        string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
        using (SqlConnection con = new SqlConnection(constr))
        {
            using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers"))
            {
                using (SqlDataAdapter sda = new SqlDataAdapter())
                {
                    cmd.Connection = con;
                    sda.SelectCommand = cmd;
                    using (DataTable dt = new DataTable())
                    {
                        sda.Fill(dt);

                        //Build the CSV file data as a Comma separated string.
                        string csv = string.Empty;

                        foreach (DataColumn column in dt.Columns)
                        {
                            //Add the Header row for CSV file.
                            csv += column.ColumnName + ',';
                        }

                        //Add new line.
                        csv += "\r\n";

                        foreach (DataRow row in dt.Rows)
                        {
                            foreach (DataColumn column in dt.Columns)
                            {
                                //Add the Data rows.
                                csv += row[column.ColumnName].ToString().Replace(",", ";") + ',';
                            }

                            //Add new line.
                            csv += "\r\n";
                        }

                        //Download the CSV file.
                        Response.Clear();
                        Response.Buffer = true;
                        Response.AddHeader("content-disposition", "attachment;filename=SqlExport.csv");
                        Response.Charset = "";
                        Response.ContentType = "application/text";
                        Response.Output.Write(csv);
                        Response.Flush();
                        Response.End();
                    }
                }
            }
        }
    }
}

por que nenhum construtor de strings aqui?
Seabizkit

1
Qual RFC você usou para escrever isso .Replace(",", ";") + ',' :?
precisa

Desta forma, leva muito tempo se você tiver mais de 20.000 linhas e 30 colunas
Vikrant

3

Manipulação de vírgulas

Para manipular vírgulas dentro dos valores ao usar string.Format(...), o seguinte funcionou para mim:

var newLine = string.Format("\"{0}\",\"{1}\",\"{2}\"",
                              first,
                              second,
                              third                                    
                              );
csv.AppendLine(newLine);

Então, para combiná-lo com a resposta de Johan, ficaria assim:

//before your loop
var csv = new StringBuilder();

//in your loop
  var first = reader[0].ToString();
  var second = image.ToString();
  //Suggestion made by KyleMit
  var newLine = string.Format("\"{0}\",\"{1}\"", first, second);
  csv.AppendLine(newLine);  

//after your loop
File.WriteAllText(filePath, csv.ToString());

Retornando arquivo CSV

Se você simplesmente deseja retornar o arquivo em vez de gravá-lo em um local, este é um exemplo de como eu o realizei:

De um procedimento armazenado

public FileContentResults DownloadCSV()
{
  // I have a stored procedure that queries the information I need
  SqlConnection thisConnection = new SqlConnection("Data Source=sv12sql;User ID=UI_Readonly;Password=SuperSecure;Initial Catalog=DB_Name;Integrated Security=false");
  SqlCommand queryCommand = new SqlCommand("spc_GetInfoINeed", thisConnection);
  queryCommand.CommandType = CommandType.StoredProcedure;

  StringBuilder sbRtn = new StringBuilder();

  // If you want headers for your file
  var header = string.Format("\"{0}\",\"{1}\",\"{2}\"",
                             "Name",
                             "Address",
                             "Phone Number"
                            );
  sbRtn.AppendLine(header);

  // Open Database Connection
  thisConnection.Open();
  using (SqlDataReader rdr = queryCommand.ExecuteReader())
  {
    while (rdr.Read())
    {
      // rdr["COLUMN NAME"].ToString();
      var queryResults = string.Format("\"{0}\",\"{1}\",\"{2}\"",
                                        rdr["Name"].ToString(),
                                        rdr["Address"}.ToString(),
                                        rdr["Phone Number"].ToString()
                                       );
      sbRtn.AppendLine(queryResults);
    }
  }
  thisConnection.Close();

  return File(new System.Text.UTF8Encoding().GetBytes(sbRtn.ToString()), "text/csv", "FileName.csv");
}

De uma lista

/* To help illustrate */
public static List<Person> list = new List<Person>();

/* To help illustrate */
public class Person
{
  public string name;
  public string address;
  public string phoneNumber;
}

/* The important part */
public FileContentResults DownloadCSV()
{
  StringBuilder sbRtn = new StringBuilder();

  // If you want headers for your file
  var header = string.Format("\"{0}\",\"{1}\",\"{2}\"",
                             "Name",
                             "Address",
                             "Phone Number"
                            );
  sbRtn.AppendLine(header);

  foreach (var item in list)
  {
      var listResults = string.Format("\"{0}\",\"{1}\",\"{2}\"",
                                        item.name,
                                        item.address,
                                        item.phoneNumber
                                       );
      sbRtn.AppendLine(listResults);
    }
  }

  return File(new System.Text.UTF8Encoding().GetBytes(sbRtn.ToString()), "text/csv", "FileName.csv");
}

Espero que isso seja útil.


2

Este é um tutorial simples sobre a criação de arquivos csv usando C # que você poderá editar e expandir para atender às suas próprias necessidades.

Primeiro, você precisará criar um novo aplicativo de console do Visual Studio C #. Existem etapas a seguir para fazer isso.

O código de exemplo criará um arquivo csv chamado MyTest.csv no local que você especificar. O conteúdo do arquivo deve ter 3 colunas nomeadas com texto nas 3 primeiras linhas.

https://tidbytez.com/2018/02/06/how-to-create-a-csv-file-with-c/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CreateCsv
{
    class Program
    {
        static void Main()
        {
            // Set the path and filename variable "path", filename being MyTest.csv in this example.
            // Change SomeGuy for your username.
            string path = @"C:\Users\SomeGuy\Desktop\MyTest.csv";

            // Set the variable "delimiter" to ", ".
            string delimiter = ", ";

            // This text is added only once to the file.
            if (!File.Exists(path))
            {
                // Create a file to write to.
                string createText = "Column 1 Name" + delimiter + "Column 2 Name" + delimiter + "Column 3 Name" + delimiter + Environment.NewLine;
                File.WriteAllText(path, createText);
            }

            // This text is always added, making the file longer over time
            // if it is not deleted.
            string appendText = "This is text for Column 1" + delimiter + "This is text for Column 2" + delimiter + "This is text for Column 3" + delimiter + Environment.NewLine;
            File.AppendAllText(path, appendText);

            // Open the file to read from.
            string readText = File.ReadAllText(path);
            Console.WriteLine(readText);
        }
    }
}

2
Um link para uma solução é bem-vindo, mas garanta que sua resposta seja útil sem ela: adicione contexto ao link para que seus colegas usuários tenham uma idéia do que é e por que está lá, depois cite a parte mais relevante da página que você ' reencaminhando para o caso de a página de destino não estar disponível. Respostas que são pouco mais que um link podem ser excluídas.
Shree

Ah entendo. Obrigado pelo feedback. Fiz as alterações destacadas. Por favor, vote.
Bloggins 03/04/19


0

Aqui está outra biblioteca de código aberto para criar arquivos CSV facilmente, o Cinchoo ETL

List<dynamic> objs = new List<dynamic>();

dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = @"Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Tom";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

using (var parser = new ChoCSVWriter("emp.csv").WithFirstLineHeader())
{
    parser.Write(objs);
}

Para mais informações, leia o artigo do CodeProject sobre uso.


0

Uma maneira simples de se livrar do problema de substituição é usar File.AppendTextpara acrescentar linhas no final do arquivo, como

void Main()
{
    using (System.IO.StreamWriter sw = System.IO.File.AppendText("file.txt"))
    {          
        string first = reader[0].ToString();
        string second=image.ToString();
        string csv = string.Format("{0},{1}\n", first, second);
        sw.WriteLine(csv);
    }
} 

0
enter code here

string string_value = string.Empty;

        for (int i = 0; i < ur_grid.Rows.Count; i++)
        {
            for (int j = 0; j < ur_grid.Rows[i].Cells.Count; j++)
            {
                if (!string.IsNullOrEmpty(ur_grid.Rows[i].Cells[j].Text.ToString()))
                {
                    if (j > 0)
                        string_value= string_value+ "," + ur_grid.Rows[i].Cells[j].Text.ToString();
                    else
                    {
                        if (string.IsNullOrEmpty(string_value))
                            string_value= ur_grid.Rows[i].Cells[j].Text.ToString();
                        else
                            string_value= string_value+ Environment.NewLine + ur_grid.Rows[i].Cells[j].Text.ToString();
                    }
                }
            }
        }


        string where_to_save_file = @"d:\location\Files\sample.csv";
        File.WriteAllText(where_to_save_file, string_value);

        string server_path = "/site/Files/sample.csv";
        Response.ContentType = ContentType;
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(server_path));
        Response.WriteFile(server_path);
        Response.End();

0
public static class Extensions
{
    public static void WriteCSVLine(this StreamWriter writer, IEnumerable<string> fields)
    {
        const string q = @"""";
        writer.WriteLine(string.Join(",",
            fields.Select(
                v => (v.Contains(',') || v.Contains('"') || v.Contains('\n') || v.Contains('\r')) ? $"{q}{v.Replace(q, q + q)}{q}" : v
                )));
    }

    public static void WriteFields(this StreamWriter writer, params string[] fields) => WriteFields(writer, (IEnumerable<string>)fields);
}

Isso deve permitir que você escreva um arquivo csv de maneira bastante simples. Uso:

StreamWriter writer = new StreamWriter("myfile.csv");
writer.WriteCSVLine(new[]{"A", "B"});
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.