C ++에서 CSV 파일 데이터를로드하고 사용해야합니다. 이 시점에서 실제로는 쉼표로 구분 된 파서 일 수 있습니다 (즉, 새 줄과 쉼표를 피하는 것에 대해 걱정하지 마십시오). 주요 요구 사항은 한 줄씩 파서인데,이 메서드는 메서드가 호출 될 때마다 다음 줄의 벡터를 반환합니다.
나는 유망하게 보이는이 기사를 발견했다 :
http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp
나는 Boost ‘s Spirit을 사용한 적이 없지만 그것을 기꺼이 시도합니다. 그러나 더 간단한 해결책이없는 경우에만 간과하고 있습니다.
답변
쉼표와 줄 바꿈을 신경 쓰지 않고
쉼표와 줄 바꿈을 따옴표로 묶을 수 없다면 (이스케이프 할 수 없다면 …)
약 세 줄의 코드 (OK 14-> 그러나 전체 파일을 읽으려면 15입니다.)
std::vector<std::string> getNextLineAndSplitIntoTokens(std::istream& str)
{
std::vector<std::string> result;
std::string line;
std::getline(str,line);
std::stringstream lineStream(line);
std::string cell;
while(std::getline(lineStream,cell, ','))
{
result.push_back(cell);
}
// This checks for a trailing comma with no data after it.
if (!lineStream && cell.empty())
{
// If there was a trailing comma then add an empty element.
result.push_back("");
}
return result;
}
그냥 행을 나타내는 클래스를 만들 것입니다.
그런 다음 해당 객체로 스트리밍하십시오.
#include <iterator>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
class CSVRow
{
public:
std::string const& operator[](std::size_t index) const
{
return m_data[index];
}
std::size_t size() const
{
return m_data.size();
}
void readNextRow(std::istream& str)
{
std::string line;
std::getline(str, line);
std::stringstream lineStream(line);
std::string cell;
m_data.clear();
while(std::getline(lineStream, cell, ','))
{
m_data.push_back(cell);
}
// This checks for a trailing comma with no data after it.
if (!lineStream && cell.empty())
{
// If there was a trailing comma then add an empty element.
m_data.push_back("");
}
}
private:
std::vector<std::string> m_data;
};
std::istream& operator>>(std::istream& str, CSVRow& data)
{
data.readNextRow(str);
return str;
}
int main()
{
std::ifstream file("plop.csv");
CSVRow row;
while(file >> row)
{
std::cout << "4th Element(" << row[3] << ")\n";
}
}
그러나 약간의 작업으로 기술적으로 반복자를 만들 수 있습니다.
class CSVIterator
{
public:
typedef std::input_iterator_tag iterator_category;
typedef CSVRow value_type;
typedef std::size_t difference_type;
typedef CSVRow* pointer;
typedef CSVRow& reference;
CSVIterator(std::istream& str) :m_str(str.good()?&str:NULL) { ++(*this); }
CSVIterator() :m_str(NULL) {}
// Pre Increment
CSVIterator& operator++() {if (m_str) { if (!((*m_str) >> m_row)){m_str = NULL;}}return *this;}
// Post increment
CSVIterator operator++(int) {CSVIterator tmp(*this);++(*this);return tmp;}
CSVRow const& operator*() const {return m_row;}
CSVRow const* operator->() const {return &m_row;}
bool operator==(CSVIterator const& rhs) {return ((this == &rhs) || ((this->m_str == NULL) && (rhs.m_str == NULL)));}
bool operator!=(CSVIterator const& rhs) {return !((*this) == rhs);}
private:
std::istream* m_str;
CSVRow m_row;
};
int main()
{
std::ifstream file("plop.csv");
for(CSVIterator loop(file); loop != CSVIterator(); ++loop)
{
std::cout << "4th Element(" << (*loop)[3] << ")\n";
}
}
답변
Boost Tokenizer를 사용하는 솔루션 :
std::vector<std::string> vec;
using namespace boost;
tokenizer<escaped_list_separator<char> > tk(
line, escaped_list_separator<char>('\\', ',', '\"'));
for (tokenizer<escaped_list_separator<char> >::iterator i(tk.begin());
i!=tk.end();++i)
{
vec.push_back(*i);
}
답변
내 버전은 표준 C ++ 11 라이브러리 이외의 것을 사용하지 않습니다. Excel CSV 인용에 잘 대처합니다.
spam eggs,"foo,bar","""fizz buzz"""
1.23,4.567,-8.00E+09
이 코드는 유한 상태 머신으로 작성되었으며 한 번에 한 문자 씩 소비합니다. 추론하기가 더 쉽다고 생각합니다.
#include <istream>
#include <string>
#include <vector>
enum class CSVState {
UnquotedField,
QuotedField,
QuotedQuote
};
std::vector<std::string> readCSVRow(const std::string &row) {
CSVState state = CSVState::UnquotedField;
std::vector<std::string> fields {""};
size_t i = 0; // index of the current field
for (char c : row) {
switch (state) {
case CSVState::UnquotedField:
switch (c) {
case ',': // end of field
fields.push_back(""); i++;
break;
case '"': state = CSVState::QuotedField;
break;
default: fields[i].push_back(c);
break; }
break;
case CSVState::QuotedField:
switch (c) {
case '"': state = CSVState::QuotedQuote;
break;
default: fields[i].push_back(c);
break; }
break;
case CSVState::QuotedQuote:
switch (c) {
case ',': // , after closing quote
fields.push_back(""); i++;
state = CSVState::UnquotedField;
break;
case '"': // "" -> "
fields[i].push_back('"');
state = CSVState::QuotedField;
break;
default: // end of quote
state = CSVState::UnquotedField;
break; }
break;
}
}
return fields;
}
/// Read CSV file, Excel dialect. Accept "quoted fields ""with quotes"""
std::vector<std::vector<std::string>> readCSV(std::istream &in) {
std::vector<std::vector<std::string>> table;
std::string row;
while (!in.eof()) {
std::getline(in, row);
if (in.bad() || in.fail()) {
break;
}
auto fields = readCSVRow(row);
table.push_back(fields);
}
return table;
}
답변
C ++ 문자열 툴킷 라이브러리 (StrTk는) 당신이 중 하나에서 데이터를로드 할 수있는 토큰 그리드 클래스가 텍스트 파일, 문자열이나 문자 버퍼 행 열 방식으로, 및 구문 분석 / 처리 그들에게 있습니다.
행 구분 기호와 열 구분 기호를 지정하거나 기본값을 사용할 수 있습니다.
void foo()
{
std::string data = "1,2,3,4,5\n"
"0,2,4,6,8\n"
"1,3,5,7,9\n";
strtk::token_grid grid(data,data.size(),",");
for(std::size_t i = 0; i < grid.row_count(); ++i)
{
strtk::token_grid::row_type r = grid.row(i);
for(std::size_t j = 0; j < r.size(); ++j)
{
std::cout << r.get<int>(j) << "\t";
}
std::cout << std::endl;
}
std::cout << std::endl;
}
더 많은 예제는 여기 에서 찾을 수 있습니다
답변
escaped_list_separator와 함께 Boost Tokenizer를 사용할 수 있습니다.
escaped_list_separator 는 csv의 상위 집합을 구문 분석합니다. 부스트 :: 토큰 화기
부스트 토크 나이저 헤더 파일 만 사용하며 부스트 라이브러리에 대한 링크는 필요하지 않습니다.
다음은 예입니다 (자세한 내용은 C ++ 에서 Boost Tokenizer로 CSV 파일 구문 분석 또는 참조 Boost::tokenizer
).
#include <iostream> // cout, endl
#include <fstream> // fstream
#include <vector>
#include <string>
#include <algorithm> // copy
#include <iterator> // ostream_operator
#include <boost/tokenizer.hpp>
int main()
{
using namespace std;
using namespace boost;
string data("data.csv");
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());
// vector now contains strings from one row, output to cout here
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, "|"));
cout << "\n----------------------" << endl;
}
}
답변
CSV를 구문 분석하는 데 Spirit을 사용하는 것은 과도하지 않습니다. Spirit은 마이크로 파싱 작업에 적합합니다. 예를 들어 Spirit 2.1을 사용하면 다음과 같이 쉽습니다.
bool r = phrase_parse(first, last,
// Begin grammar
(
double_ % ','
)
,
// End grammar
space, v);
벡터 v는 값으로 채워집니다. 일련의 튜토리얼이 있습니다Boost 1.41과 함께 출시 된 새로운 Spirit 2.1 문서 .
튜토리얼은 단순에서 복합으로 진행됩니다. CSV 파서는 가운데 어딘가에 표시되며 Spirit 사용에 대한 다양한 기술을 다룹니다. 생성 된 코드는 직접 작성한 코드만큼 빡빡합니다. 생성 된 어셈블러를 확인하십시오!
답변
당신이 경우 DO 제대로 CSV 구문 분석에 대한주의를,이 그것을 할 것입니다 … 비교적 천천히 한 번에 하나 개의 문자를 작동한다.
void ParseCSV(const string& csvSource, vector<vector<string> >& lines)
{
bool inQuote(false);
bool newLine(false);
string field;
lines.clear();
vector<string> line;
string::const_iterator aChar = csvSource.begin();
while (aChar != csvSource.end())
{
switch (*aChar)
{
case '"':
newLine = false;
inQuote = !inQuote;
break;
case ',':
newLine = false;
if (inQuote == true)
{
field += *aChar;
}
else
{
line.push_back(field);
field.clear();
}
break;
case '\n':
case '\r':
if (inQuote == true)
{
field += *aChar;
}
else
{
if (newLine == false)
{
line.push_back(field);
lines.push_back(line);
field.clear();
line.clear();
newLine = true;
}
}
break;
default:
newLine = false;
field.push_back(*aChar);
break;
}
aChar++;
}
if (field.size())
line.push_back(field);
if (line.size())
lines.push_back(line);
}