Довольно понятный, я попробовал google и получил много страшного expertsexchange, я искал здесь также безрезультатно. Лучше всего было бы использовать онлайн-учебник или пример. Спасибо, ребята.
Как я могу читать и редактировать данные файла CSV на С++?
Ответ 1
Если то, что вы действительно делаете, это манипулирование самим файлом CSV, ответ Nelson имеет смысл. Однако мое подозрение в том, что CSV - это просто артефакт проблемы, которую вы решаете. В С++ это, вероятно, означает, что у вас есть что-то вроде этой модели данных:
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;
};
Таким образом, когда вы работаете с набором данных, имеет смысл иметь std::vector<Customer>
или std::set<Customer>
.
С учетом этого подумайте о том, что ваша обработка CSV выполняется как две операции:
// 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);
Чтение и запись одной строки за раз, а не сохранение полного представления в файле самого файла. Есть несколько очевидных преимуществ:
- Ваши данные представлены в форме, которая имеет смысл для вашей проблемы (клиентов), а не для текущего решения (файлы CSV).
- Вы можете тривиально добавлять адаптеры для других форматов данных, таких как объемный импорт/экспорт SQL, файлы электронной таблицы Excel/OO или даже рендеринг HTML
<table>
. - Объем вашей памяти, вероятно, будет меньше (зависит от относительного
sizeof(Customer)
и количества байтов в одной строке). -
CSVReader
иCSVWriter
могут быть повторно использованы в качестве основы для модели памяти (например, Nelson's) без потери производительности или функциональности. Обратное неверно.
Ответ 2
Дополнительная информация была бы полезной.
Но простейшая форма:
#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!!!!
}
}
}
Также см. этот вопрос: парсер CSV на С++
Ответ 3
Вы можете попробовать библиотеку Boost Tokenizer, в частности разделитель списков экранов
Ответ 4
В свое время я работал с большим количеством файлов CSV. Я хотел бы добавить совет:
1 - В зависимости от источника (Excel и т.д.) в поле могут быть встроены запятые или вкладки. Обычно правило состоит в том, что они будут "защищены", потому что поле будет ограничено двойными кавычками, как в "Boston, MA 02346".
2 - Некоторые источники не будут дублировать все текстовые поля. Другие источники. Другие будут разграничивать все поля, даже цифры.
3 - Поля, содержащие двойные кавычки, обычно получают удвоенные вложенные двойные кавычки (и само поле ограничено двойными кавычками, как в "Джордже" "Babe" "Ruth".
4 - Некоторые источники будут включать CR/LF (один из них - Excel!). Иногда это будет просто CR. Поле обычно будет ограничено двойными кавычками, но эту ситуацию очень сложно обрабатывать.
Ответ 5
Это хорошее упражнение для работы над:)
Вы должны разбить свою библиотеку на три части.
- Загрузка файла CSV
- Представление файла в памяти, чтобы вы могли его изменить и прочитать.
- Сохранение файла CSV на диск
Итак, вы смотрите на создание класса CSVDocument, который содержит:
- Загрузить (const char * файл);
- Сохранить (const char * файл);
- GetBody
Чтобы вы могли использовать свою библиотеку следующим образом:
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");
Что дает нам следующие интерфейсы:
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);
};
Теперь вам просто нужно заполнить пробелы отсюда:)
Поверьте мне, когда я это скажу, инвестируя свое время в изучение того, как создавать библиотеки, особенно те, которые имеют дело с загрузкой, манипулированием и сохранением данных, не только устранит вашу зависимость от существования таких библиотек, но и сделает вас универсальный лучший программист.
:)
ИЗМЕНИТЬ
Я не знаю, сколько вы уже знаете о манипуляциях с строками и синтаксическом анализе; поэтому, если вы застряли, я с удовольствием помогу.
Ответ 6
Вот какой код вы можете использовать. Данные из csv хранятся внутри массива строк. Каждая строка представляет собой массив строк. Надеюсь, это поможет.
#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;
}
}
Ответ 7
Посмотрите на Практика программирования '(TPOP) Кернигана и Пайка. Он включает пример разбора CSV файлов в C и С++. Но стоило бы прочитать книгу, даже если вы не используете код.
(Предыдущий URL: http://cm.bell-labs.com/cm/cs/tpop/)
Ответ 8
Использование boost tokenizer для анализа записей, подробнее см. здесь.
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;
}
Ответ 9
Я нашел этот интересный подход:
Цитата: CSVtoC - это программа, которая берет CSV или файл значений разделенных запятыми в качестве входных данных и выдает их как структуру C.
Естественно, вы не можете вносить изменения в CSV файл, но если вам просто нужен доступ к данным только для чтения в памяти, он может работать.