HOWTO · Csharp
C# Leia um arquivo CSV e armazene seus valores em uma array
Este artigo de como fazer mostra diferentes métodos para ler um arquivo CSV e armazenar seus valores em um array em C#. Ele introduz métodos como OpenRead(), ReadLine() e TextFieldParser
Nesta página
Um arquivo CSV é um arquivo separado por vírgulas, que é utilizado para armazenar dados de forma organizada. Ele normalmente armazena os dados em forma de tabela. A maioria das organizações empresariais armazena seus dados em arquivos CSV.
Em C#, podemos realizar várias operações em um arquivo CSV. Podemos ler, gravar e alterar um arquivo CSV. Este artigo concentra-se em ler um arquivo CSV e armazenar seus valores em um array: ler linhas uma a uma com StreamReader, carregar todas as linhas de uma vez com File.ReadAllLines() e analisar campos com TextFieldParser.
Os três primeiros exemplos usam o mesmo arquivo de exemplo Data.csv:
Name,Age
Alice,30
Bob,25
Ler um arquivo CSV com StreamReader
A classe StreamReader lê um arquivo como um fluxo de texto. File.OpenRead() abre o arquivo e ReadLine() retorna uma linha de cada vez até que EndOfStream seja atingido. Dividir cada linha na vírgula fornece os valores individuais, que o exemplo armazena em uma lista por coluna.
A sintaxe correta para usar os métodos OpenRead() e ReadLine() é a seguinte:
// OpenRead() Syntax
File.OpenRead(@"FilePath");
// ReadLine() Syntax
StreamReaderObject.ReadLine();
Código de exemplo:
using System.IO;
using System.Collections.Generic;
using System;
class ReadingCSV {
static void Main(string[] args) {
var reader = new StreamReader(File.OpenRead(@"D:\New folder\Data.csv"));
List<string> listA = new List<string>();
List<string> listB = new List<string>();
while (!reader.EndOfStream) {
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
}
foreach (var column1 in listA) {
Console.WriteLine(column1);
}
foreach (var column2 in listB) {
Console.WriteLine(column2);
}
}
}
Saída:
Name
Alice
Bob
Age
30
25
Dividir na vírgula funciona quando nenhum campo contém ele próprio uma vírgula. Um campo entre aspas como "Smith, John" seria dividido no lugar errado; use TextFieldParser para arquivos assim.
Ler todas as linhas de uma vez com File.ReadAllLines
Quando o arquivo é pequeno o suficiente para caber na memória, File.ReadAllLines() é o caminho mais curto até um array: ele lê o arquivo inteiro e retorna cada linha como um elemento de um string[]. O exemplo abaixo divide cada linha, armazena cada linha de dados como seu próprio string[] e converte a lista de linhas em um array irregular com ToArray(), de modo que data[i] é a linha completa i. O loop começa no índice 1 para ignorar a linha de cabeçalho, ignora linhas em branco e verifica a quantidade de campos antes de indexar, portanto uma linha curta ou vazia não pode derrubá-lo.
Código de exemplo:
using System;
using System.Collections.Generic;
using System.IO;
class ReadingAllLines {
static void Main() {
string[] lines = File.ReadAllLines("Data.csv");
List<string[]> rows = new List<string[]>();
// Start at index 1 to skip the header row.
for (int i = 1; i < lines.Length; i++) {
if (string.IsNullOrWhiteSpace(lines[i])) {
continue;
}
string[] values = lines[i].Split(',');
if (values.Length < 2) {
continue;
}
rows.Add(values);
}
string[][] data = rows.ToArray();
foreach (string[] row in data) {
Console.WriteLine(row[0] + ": " + row[1]);
}
}
}
Saída:
Alice: 30
Bob: 25
O compromisso é memória versus simplicidade. ReadAllLines() carrega o arquivo inteiro antes que a primeira linha seja processada e retorna um array que você pode indexar, contar e reler imediatamente. O loop com StreamReader mantém apenas uma linha por vez, portanto continua sendo a escolha mais segura para arquivos muito grandes cujas linhas você processa uma vez e descarta. Para arquivos pequenos que você consulta mais de uma vez, carregar tudo de uma vez é mais simples e rápido o bastante.
Analisar campos CSV com TextFieldParser
A classe TextFieldParser é definida no namespace Microsoft.VisualBasic.FileIO. Ela analisa um arquivo delimitado campo a campo e compreende campos entre aspas. Defina os delimitadores com SetDelimiters() e depois chame ReadFields() para obter os campos da linha atual como um array de strings; o analisador passa para a próxima linha a cada chamada. Em um projeto .NET Framework, adicione uma referência a Microsoft.VisualBasic antes de executar o programa abaixo; no .NET moderno, o namespace faz parte do framework compartilhado.
A sintaxe correta para usar este analisador é a seguinte:
TextFieldParser ParserName = new TextFieldParser(PathString);
Código de exemplo:
using System;
using System.Collections.Generic;
using Microsoft.VisualBasic.FileIO;
class ReadingCSV {
public static void Main() {
var path = @"D:\New folder\Data.csv";
List<string> listA = new List<string>();
List<string> listB = new List<string>();
using (TextFieldParser csvReader = new TextFieldParser(path)) {
csvReader.CommentTokens = new string[] { "#" };
csvReader.SetDelimiters(new string[] { "," });
csvReader.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
csvReader.ReadLine();
while (!csvReader.EndOfData) {
// Read current line fields, pointer moves to the next line.
string[] fields = csvReader.ReadFields();
listA.Add(fields[0]);
listB.Add(fields[1]);
}
}
foreach (var column1 in listA) {
Console.WriteLine(column1);
}
foreach (var column2 in listB) {
Console.WriteLine(column2);
}
}
}
Saída:
Alice
Bob
30
25
Desta vez, a linha de cabeçalho é ignorada com o ReadLine() inicial, e o analisador, e não uma divisão manual, decide onde cada campo termina, de modo que uma vírgula entre aspas dentro de um campo permanece intacta.
Tratar campos entre aspas e aspas escapadas
As exportações reais colocam os campos entre aspas. Um campo que contém o delimitador é envolvido em aspas duplas, e uma aspa dupla dentro de um campo entre aspas é escrita duas vezes. O arquivo de exemplo People.csv abaixo usa as duas regras:
Name,Note
"Smith, John","Likes ""quoted"" text"
Jones,Plain note
Split(',') nada sabe sobre essas regras: ele corta a primeira linha de dados em 3 partes na vírgula dentro do nome. TextFieldParser as aplica: ele remove as aspas que envolvem o campo e transforma cada aspa duplicada em uma só, de modo que os campos voltam exatamente como foram concebidos. O programa imprime primeiro a quantidade de partes de Split(',') e depois os campos analisados.
Código de exemplo:
using System;
using Microsoft.VisualBasic.FileIO;
class ReadingQuotedCsv {
public static void Main() {
string rawLine = "\"Smith, John\",\"Likes \"\"quoted\"\" text\"";
Console.WriteLine("Split(',') pieces: " + rawLine.Split(',').Length);
using (TextFieldParser parser = new TextFieldParser("People.csv")) {
parser.SetDelimiters(new string[] { "," });
parser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
parser.ReadLine();
while (!parser.EndOfData) {
string[] fields = parser.ReadFields();
Console.WriteLine(fields[0] + " | " + fields[1]);
}
}
}
}
Saída:
Split(',') pieces: 3
Smith, John | Likes "quoted" text
Jones | Plain note
Se uma divisão manual produzir mais campos do que o cabeçalho tem colunas, a causa usual são os campos entre aspas. Troque esse arquivo para TextFieldParser em vez de tentar consertar as partes depois.
Erros comuns ao ler um arquivo CSV
-
IndexOutOfRangeExceptionemvalues[1]: uma linha final em branco ou uma linha com uma coluna faltando se divide em menos campos do que o código espera. Ignore linhas só de espaços em branco e verifique a quantidade de campos antes de indexar, como faz o exemplo deReadAllLines(). -
FileNotFoundExceptionouDirectoryNotFoundException: o caminho não existe na máquina que executa o programa. Um caminho absoluto codificado comoD:\New folder\Data.csvsó funciona no computador do autor; prefira um caminho relativo ou monte o caminho comPath.Combine(), e verifique-o comFile.Exists()durante a depuração. -
A linha de cabeçalho aparece como dado: o primeiro exemplo com
StreamReaderarmazenaNameeAgejunto com as linhas reais. Quando um arquivo tem cabeçalho, ignore a primeira linha antes do loop, como fazem os outros exemplos. -
O arquivo permanece bloqueado: um
StreamReaderque nunca é fechado mantém seu identificador até o coletor de lixo passar. Declare o leitor em um blocousingpara que o arquivo seja fechado assim que o loop terminar, mesmo quando uma exceção o interrompa.