Como posso ler e manipular dados de arquivos CSV em C ++? [duplicado]
Pergunta
Esta questão já tem uma resposta aqui:
- Como posso ler e arquivos CSV de análise em C ++? 33 respostas
Pretty auto-explicativo, eu tentei google e tem um monte de expertsexchange temida, eu procurei aqui também sem sucesso. Um tutorial online ou exemplo seria melhor. Obrigado rapazes.
Solução
Se o que você está fazendo realmente está manipulando um arquivo CSV em si, a resposta de Nelson faz sentido. No entanto, a minha suspeita é que o CSV é simplesmente um artefato do problema que você está resolvendo. Em C ++, isso provavelmente significa que você tem algo assim como seu modelo de dados:
struct Customer {
int id;
std::string first_name;
std::string last_name;
struct {
std::string street;
std::string unit;
} address;
char state[2];
int zip;
};
Assim, quando você está trabalhando com uma coleção de dados, faz sentido ter std::vector<Customer>
ou std::set<Customer>
.
Com isso em mente, pense em seu CSV manipulação como duas operações:
// if you wanted to go nuts, you could use a forward iterator concept for both of these
class CSVReader {
public:
CSVReader(const std::string &inputFile);
bool hasNextLine();
void readNextLine(std::vector<std::string> &fields);
private:
/* secrets */
};
class CSVWriter {
public:
CSVWriter(const std::string &outputFile);
void writeNextLine(const std::vector<std::string> &fields);
private:
/* more secrets */
};
void readCustomers(CSVReader &reader, std::vector<Customer> &customers);
void writeCustomers(CSVWriter &writer, const std::vector<Customer> &customers);
Ler e escrever uma única linha de cada vez, em vez de manter uma representação completa na memória do próprio arquivo. Existem alguns benefícios óbvios:
- Seus dados são representados em uma forma que faça sentido para o seu problema (clientes), mais do que a solução atual (arquivos CSV).
- Você pode trivialmente adicionar adaptadores para outros formatos de dados, tais como a importação SQL granel / exportação, Excel / planilhas OO, ou até mesmo uma prestação
<table>
HTML. - O seu consumo de memória é provável que seja menor (depende
sizeof(Customer)
relativa vs. o número de bytes em uma única linha). -
CSVReader
eCSVWriter
podem ser reutilizados como a base para um modelo in-memory (como Nelson) sem perda de desempenho ou funcionalidade. O inverso não é verdadeiro.
Outras dicas
Mais informação pode ser útil.
Mas a forma mais simples:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
int main()
{
std::ifstream data("plop.csv");
std::string line;
while(std::getline(data,line))
{
std::stringstream lineStream(line);
std::string cell;
while(std::getline(lineStream,cell,','))
{
// You have a cell!!!!
}
}
}
Veja também esta pergunta: CSV analisador em C ++
Você pode tentar o impulso biblioteca Tokenizer, em particular a Escapou Separador de lista
Eu trabalhei com um monte de arquivos CSV no meu tempo. Eu gostaria de adicionar o conselho:
1 - Dependendo da fonte (Excel, etc.), vírgulas ou abas pode ser incorporado em um campo. Normalmente, a regra é que eles vão ser 'protegidos' porque o campo será double-quote delimitado, como em "Boston, MA 02346".
2 - Algumas fontes não vai double-quote delimitar todos os campos de texto. Outras fontes vão. Outros vão delimitar todos os campos, mesmo numerics.
3 -. Campos contendo aspas geralmente obter as aspas duplas incorporados dobrou (e o próprio campo delimitado com aspas duplas, como em "George '' Babe" "Ruth"
4 - Algumas fontes irá incorporar CR / LFs (Excel é uma delas!). Às vezes ele vai ser apenas um CR. O campo será normalmente aspas delimitada, mas esta situação é muito difícil de lidar.
Este é um bom exercício para si mesmo para os trabalhos sobre:)
Você deve quebrar a sua biblioteca em três partes
- Carregando o arquivo CSV
- Representando o arquivo na memória para que você possa modificá-lo e lê-lo
- Salvar o arquivo CSV de volta para o disco
Então, você está olhando para escrever uma classe CSVDocument que contém:
- Load (char const * arquivo);
- Save (char const * arquivo);
- GetBody
Para que você possa usar sua biblioteca como esta:
CSVDocument doc;
doc.Load("file.csv");
CSVDocumentBody* body = doc.GetBody();
CSVDocumentRow* header = body->GetRow(0);
for (int i = 0; i < header->GetFieldCount(); i++)
{
CSVDocumentField* col = header->GetField(i);
cout << col->GetText() << "\t";
}
for (int i = 1; i < body->GetRowCount(); i++) // i = 1 so we skip the header
{
CSVDocumentRow* row = body->GetRow(i);
for (int p = 0; p < row->GetFieldCount(); p++)
{
cout << row->GetField(p)->GetText() << "\t";
}
cout << "\n";
}
body->GetRecord(10)->SetText("hello world");
CSVDocumentRow* lastRow = body->AddRow();
lastRow->AddField()->SetText("Hey there");
lastRow->AddField()->SetText("Hey there column 2");
doc->Save("file.csv");
O que nos dá as seguintes interfaces:
class CSVDocument
{
public:
void Load(const char* file);
void Save(const char* file);
CSVDocumentBody* GetBody();
};
class CSVDocumentBody
{
public:
int GetRowCount();
CSVDocumentRow* GetRow(int index);
CSVDocumentRow* AddRow();
};
class CSVDocumentRow
{
public:
int GetFieldCount();
CSVDocumentField* GetField(int index);
CSVDocumentField* AddField(int index);
};
class CSVDocumentField
{
public:
const char* GetText();
void GetText(const char* text);
};
Agora você só tem que preencher os espaços em branco a partir daqui:)
Acredite em mim quando digo isso - investir seu tempo em aprender como fazer bibliotecas, especialmente aqueles que lidam com a carga, manipulação e armazenamento de dados, não só irá remover a sua dependência em relação à existência de tais bibliotecas, mas também vai fazer você um conjunto de cerca programador melhor.
:)
Editar
Eu não sei o quanto você já sabe sobre a manipulação de cadeia e análise; por isso, se você ficar preso Eu ficaria feliz em ajudar.
Aqui está um código que você pode usar. Os dados do CSV é armazenado dentro de uma matriz de linhas. Cada linha é uma matriz de cadeias. Espero que isso ajude.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
typedef std::string String;
typedef std::vector<String> CSVRow;
typedef CSVRow::const_iterator CSVRowCI;
typedef std::vector<CSVRow> CSVDatabase;
typedef CSVDatabase::const_iterator CSVDatabaseCI;
void readCSV(std::istream &input, CSVDatabase &db);
void display(const CSVRow&);
void display(const CSVDatabase&);
int main(){
std::fstream file("file.csv", std::ios::in);
if(!file.is_open()){
std::cout << "File not found!\n";
return 1;
}
CSVDatabase db;
readCSV(file, db);
display(db);
}
void readCSV(std::istream &input, CSVDatabase &db){
String csvLine;
// read every line from the stream
while( std::getline(input, csvLine) ){
std::istringstream csvStream(csvLine);
CSVRow csvRow;
String csvCol;
// read every element from the line that is seperated by commas
// and put it into the vector or strings
while( std::getline(csvStream, csvCol, ',') )
csvRow.push_back(csvCol);
db.push_back(csvRow);
}
}
void display(const CSVRow& row){
if(!row.size())
return;
CSVRowCI i=row.begin();
std::cout<<*(i++);
for(;i != row.end();++i)
std::cout<<','<<*i;
}
void display(const CSVDatabase& db){
if(!db.size())
return;
CSVDatabaseCI i=db.begin();
for(; i != db.end(); ++i){
display(*i);
std::cout<<std::endl;
}
}
Usando boost tokenizer com registros de análise , veja aqui para mais detalhes .
ifstream in(data.c_str());
if (!in.is_open()) return 1;
typedef tokenizer< escaped_list_separator<char> > Tokenizer;
vector< string > vec;
string line;
while (getline(in,line))
{
Tokenizer tok(line);
vec.assign(tok.begin(),tok.end());
/// do something with the record
if (vec.size() < 3) continue;
copy(vec.begin(), vec.end(),
ostream_iterator<string>(cout, "|"));
cout << "\n----------------------" << endl;
}
Look at ' The Practice of Programming ' (TPOP) por Kernighan e Pike. Ele inclui um exemplo de análise de arquivos CSV em C e C ++. Mas seria pena ler o livro, mesmo se você não usar o código.
(URL anterior: http://cm.bell-labs.com/ cm / cs / TPOP / )
Eu encontrei esta abordagem interessante:
Citação: CSVtoC é um programa que usa um arquivo CSV ou valores separados por vírgulas como entrada e devolve-lo como uma estrutura C.
Naturalmente, você não pode fazer alterações para o arquivo CSV, mas se você só precisa na memória somente leitura o acesso aos dados, que poderia funcionar.