C ++에서 CSV 파일 데이터를 어떻게 읽고 조작 할 수 있습니까? [복제하다
문제
이 질문은 이미 여기에 답이 있습니다.
꽤 자명 한, 나는 Google을 시험해 보았고, 끔찍한 ExpertsexChange를 많이 얻었습니다. 온라인 튜토리얼이나 예가 가장 좋습니다. 감사합니다.
해결책
당신이 실제로하고있는 일이 CSV 파일 자체를 조작하는 것이라면 넬슨의 대답은 의미가 있습니다. 그러나 내 의심은 CSV가 단순히 해결하는 문제의 인공물이라는 것입니다. C ++에서는 아마도 데이터 모델과 같은 것을 가지고 있음을 의미합니다.
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)의 기초로 재사용 할 수 있습니다. 대화는 사실이 아닙니다.
다른 팁
더 많은 정보가 유용 할 것입니다.
그러나 가장 간단한 형태 :
#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!!!!
}
}
}
또한이 질문을 참조하십시오. C ++의 CSV 파서
Boost Tokenizer 라이브러리, 특히 탈출 된 목록 분리기
저는 제 시간에 많은 CSV 파일과 함께 일했습니다. 조언을 추가하고 싶습니다.
1- 소스 (Excel 등)에 따라 쉼표 또는 탭이 필드에 포함될 수 있습니다. 일반적으로 규칙은 "보스턴, MA 02346"에서와 같이 필드가 두 배로 구분되기 때문에 '보호'될 것입니다.
2- 일부 소스는 모든 텍스트 필드를 두 배로 늘리지 않습니다. 다른 출처는 할 것입니다. 다른 사람들은 모든 필드, 심지어 숫자를 구분할 것입니다.
3- 이중 크기가 포함 된 필드는 일반적으로 내장 된 이중 인용문을 두 배로 늘립니다 (그리고 필드 자체는 "George" "Babe" "Ruth"에서와 같이 이중 인용문으로 구분됩니다.
4- 일부 소스는 CR/LFS를 포함합니다 (Excel은이 중 하나입니다!). 때로는 CR 일뿐입니다. 이 필드는 일반적으로 이중 쿼트가 구분되지만이 상황은 다루기가 매우 어렵습니다.
이것은 자신이 일하기에 좋은 운동입니다 :)
도서관을 세 부분으로 나누어야합니다
- CSV 파일로드
- 파일을 메모리로 표현하여 수정하고 읽을 수 있습니다.
- CSV 파일을 디스크에 다시 저장합니다
그래서 당신은 다음을 포함하는 csvdocument 클래스를 작성하고 있습니다.
- 로드 (const char* 파일);
- 저장 (const char* 파일);
- 구체
다음과 같이 도서관을 사용할 수 있습니다.
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);
};
이제 여기에서 공란을 채워야합니다 :)
내가 이것을 말할 때 나를 믿으십시오 - 라이브러리를 만드는 방법, 특히 데이터 저축을 다루는 방법을 배우는 방법을 배우는 데 시간을 투자하면 그러한 라이브러리의 존재에 대한 의존도를 제거 할뿐만 아니라 당신을 모두로 만들 것입니다. 더 나은 프로그래머 주변.
:)
편집하다
나는 당신이 현악 조작과 구문 분석에 대해 이미 얼마나 알고 있는지 모르겠습니다. 그래서 당신이 붙어 있다면 나는 기꺼이 도와 드릴 것입니다.
다음은 사용할 수있는 몇 가지 코드입니다. 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;
}
}
부스트 토 케이저를 사용하여 레코드를 구문 분석합니다, 자세한 내용은 여기를 참조하십시오.
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;
}
보다 '프로그래밍 연습'(TPOP)의 Kernighan & Pike. C 및 C ++ 모두에서 CSV 파일을 구문 분석하는 예가 포함되어 있습니다. 그러나 코드를 사용하지 않더라도 책을 읽을 가치가 있습니다.
(이전 URL : http://cm.bell-labs.com/cm/cs/tpop/)
나는이 흥미로운 접근법을 발견했다.
인용구 : CSVTOC은 CSV 또는 쉼표로 분리 된 값 파일을 입력으로 가져 와서 C 구조로 덤프하는 프로그램입니다.
당연히 CSV 파일을 변경할 수는 없지만 데이터에 대한 메모리 내 읽기 전용 액세스가 필요한 경우 작동 할 수 있습니다.