HOWTO · C++
Ler arquivo CSV em C++
Este artigo demonstra como ler um arquivo CSV em C++
Nesta página
Para ler em C++ um arquivo simples com campos separados por vírgulas, leia uma linha de cada vez com std::getline e, em seguida, divida essa linha em campos com uma segunda chamada a std::getline. Essa abordagem funciona quando cada linha física corresponde a um registro e as vírgulas servem apenas para separar campos. O exemplo abaixo cria seu próprio arquivo de entrada pequeno, para que você possa compilá-lo e executá-lo sem preparar um arquivo de dados separado.
Leia um arquivo CSV com std::getline e std::istringstream
Primeiro, o programa grava dois registros em grades.csv e depois abre o arquivo para leitura. O std::getline externo lê uma linha física em um std::string; o std::istringstream transforma essa linha em um fluxo, e o std::getline interno usa a vírgula como delimitador para extrair cada campo. Cada registro se torna um std::vector<std::string>, e o conjunto de registros fica em outro vetor. Assim, a ordem original é preservada, sem precisar usar um mapa indexado por inteiros quando as linhas são simplesmente sequenciais.
Execute este programa autocontido. Os rótulos Name e Score e a saída exibida permanecem em inglês, pois o exemplo é preservado sem alterações.
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
std::string trimEnds(const std::string& value) {
const auto first = std::find_if_not(value.begin(), value.end(),
[](unsigned char ch) { return std::isspace(ch); });
const auto last = std::find_if_not(value.rbegin(), value.rend(),
[](unsigned char ch) { return std::isspace(ch); }).base();
if (first >= last) {
return "";
}
return std::string(first, last);
}
int main() {
const std::string path = "grades.csv";
{
std::ofstream sample(path);
if (!sample) {
std::cerr << "Could not create input file\n";
return 1;
}
sample << "Name, Score\nAda Lovelace, 98 \nGrace Hopper, 95\n";
}
std::ifstream input(path);
if (!input) {
std::cerr << "Could not open input file\n";
return 1;
}
std::vector<std::vector<std::string>> rows;
std::string record;
while (std::getline(input, record)) {
std::istringstream line(record);
std::vector<std::string> fields;
std::string field;
while (std::getline(line, field, ',')) {
fields.push_back(trimEnds(field));
}
rows.push_back(fields);
}
for (const auto& row : rows) {
std::cout << row[0] << " | " << row[1] << '\n';
}
return 0;
}
O resultado fornecido para a execução bem-sucedida é:
Name | Score
Ada Lovelace | 98
Grace Hopper | 95
Se a criação ou a abertura do arquivo falhar, o programa escreve uma mensagem de erro em std::cerr e retorna o status 1; no fluxo normal, retorna o status 0. No seu programa, verifique se o fluxo de entrada foi aberto com sucesso antes de começar a leitura. Se o arquivo vier de fora do programa, informe o caminho e trate a falha de abertura conforme as necessidades da aplicação, em vez de presumir que o arquivo existe.
Trate espaços em branco nos campos CSV
O tratamento de espaços em branco é uma escolha sobre os dados, não uma etapa automática da separação. trimEnds remove espaços em branco apenas do início e do fim de cada campo extraído. Os espaços dentro de um valor, como Ada Lovelace, são preservados. A conversão para unsigned char antes da chamada a std::isspace evita passar um valor negativo de char simples para essa função de classificação de caracteres.
Em alguns arquivos, os espaços após uma vírgula são apenas formatação; em outros, fazem parte do campo. Aplique a remoção de espaços somente se isso corresponder ao contrato dos dados que você precisa preservar. Remover todos os caracteres de espaço em branco também juntaria as palavras de um nome ou endereço; remover os espaços das duas extremidades altera o conteúdo inicial e final. Se os espaços tiverem significado na sua fonte, mantenha o campo como foi lido ou implemente uma regra mais restrita, como remover apenas espaços ASCII.
O laço de separação também tem uma consequência simples para campos vazios: duas vírgulas consecutivas entre outros conteúdos produzem uma string vazia entre elas. Uma vírgula no fim da linha não faz com que este laço de std::getline acrescente um campo vazio final; trate explicitamente o delimitador final se o formato da sua entrada exigir esse campo. Mantenha ou rejeite valores vazios de acordo com as colunas esperadas. Uma linha com menos campos que o cabeçalho deve ser tratada explicitamente antes de acessar os elementos por índice; o breve laço de exibição acima pressupõe que as duas colunas estão presentes.
Entenda os limites da análise baseada em delimitadores
Este método divide cada linha física em todas as vírgulas. Ele não interpreta as aspas do CSV. Por exemplo, um valor como "Paris, France" é dividido em duas partes, embora um leitor de CSV deva tratar o texto entre aspas como um único campo. Aspas escapadas dentro de campos entre aspas também não são decodificadas, e uma quebra de linha dentro de um campo entre aspas é confundida com o fim de um registro. Os registros também podem ter quantidades diferentes de campos; portanto, não presuma que todas as linhas tenham o mesmo número de vírgulas.
Use essa abordagem para entradas simples e controladas cujas regras garantam um registro por linha e nenhum delimitador entre aspas. Para arquivos CSV de uso geral, use um analisador de CSV que implemente o dialeto recebido, incluindo suas regras de aspas e escape. Depois da análise, ainda valide a quantidade e o significado dos campos exigidos pela aplicação. As operações de fluxo padrão mostradas aqui leem arquivos e linhas, mas não implementam por si só toda a gramática do formato CSV.