เมื่อคำถาม CSV ดูเหมือนจะถูกนำไปที่นี่ฉันคิดว่าฉันโพสต์คำตอบของฉันที่นี่ คำตอบนี้ไม่ได้ตอบคำถามของผู้ถามโดยตรง ฉันต้องการให้สามารถอ่านในสตรีมที่ทราบว่าอยู่ในรูปแบบ CSV และยังเป็นที่รู้จักประเภทของแต่ละฟิลด์แล้ว แน่นอนว่าวิธีการด้านล่างนี้สามารถใช้ในการจัดการทุกฟิลด์ให้เป็นประเภทสตริงได้
เป็นตัวอย่างของวิธีที่ฉันต้องการใช้สตรีมอินพุต CSV พิจารณาอินพุตต่อไปนี้ (นำมาจากหน้าของวิกิพีเดียใน CSV ):
const char input[] =
"Year,Make,Model,Description,Price\n"
"1997,Ford,E350,\"ac, abs, moon\",3000.00\n"
"1999,Chevy,\"Venture \"\"Extended Edition\"\"\",\"\",4900.00\n"
"1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",\"\",5000.00\n"
"1996,Jeep,Grand Cherokee,\"MUST SELL!\n\
air, moon roof, loaded\",4799.00\n"
;
จากนั้นฉันอยากจะอ่านข้อมูลในลักษณะนี้:
std::istringstream ss(input);
std::string title[5];
int year;
std::string make, model, desc;
float price;
csv_istream(ss)
>> title[0] >> title[1] >> title[2] >> title[3] >> title[4];
while (csv_istream(ss)
>> year >> make >> model >> desc >> price) {
//...do something with the record...
}
นี่คือทางออกที่ฉันได้ลงเอยด้วย
struct csv_istream {
std::istream &is_;
csv_istream (std::istream &is) : is_(is) {}
void scan_ws () const {
while (is_.good()) {
int c = is_.peek();
if (c != ' ' && c != '\t') break;
is_.get();
}
}
void scan (std::string *s = 0) const {
std::string ws;
int c = is_.get();
if (is_.good()) {
do {
if (c == ',' || c == '\n') break;
if (s) {
ws += c;
if (c != ' ' && c != '\t') {
*s += ws;
ws.clear();
}
}
c = is_.get();
} while (is_.good());
if (is_.eof()) is_.clear();
}
}
template <typename T, bool> struct set_value {
void operator () (std::string in, T &v) const {
std::istringstream(in) >> v;
}
};
template <typename T> struct set_value<T, true> {
template <bool SIGNED> void convert (std::string in, T &v) const {
if (SIGNED) v = ::strtoll(in.c_str(), 0, 0);
else v = ::strtoull(in.c_str(), 0, 0);
}
void operator () (std::string in, T &v) const {
convert<is_signed_int<T>::val>(in, v);
}
};
template <typename T> const csv_istream & operator >> (T &v) const {
std::string tmp;
scan(&tmp);
set_value<T, is_int<T>::val>()(tmp, v);
return *this;
}
const csv_istream & operator >> (std::string &v) const {
v.clear();
scan_ws();
if (is_.peek() != '"') scan(&v);
else {
std::string tmp;
is_.get();
std::getline(is_, tmp, '"');
while (is_.peek() == '"') {
v += tmp;
v += is_.get();
std::getline(is_, tmp, '"');
}
v += tmp;
scan();
}
return *this;
}
template <typename T>
const csv_istream & operator >> (T &(*manip)(T &)) const {
is_ >> manip;
return *this;
}
operator bool () const { return !is_.fail(); }
};
ด้วยตัวช่วยดังต่อไปนี้ที่อาจทำให้เข้าใจง่ายขึ้นโดยเทมเพลตคุณลักษณะใหม่ใน C ++ 11:
template <typename T> struct is_signed_int { enum { val = false }; };
template <> struct is_signed_int<short> { enum { val = true}; };
template <> struct is_signed_int<int> { enum { val = true}; };
template <> struct is_signed_int<long> { enum { val = true}; };
template <> struct is_signed_int<long long> { enum { val = true}; };
template <typename T> struct is_unsigned_int { enum { val = false }; };
template <> struct is_unsigned_int<unsigned short> { enum { val = true}; };
template <> struct is_unsigned_int<unsigned int> { enum { val = true}; };
template <> struct is_unsigned_int<unsigned long> { enum { val = true}; };
template <> struct is_unsigned_int<unsigned long long> { enum { val = true}; };
template <typename T> struct is_int {
enum { val = (is_signed_int<T>::val || is_unsigned_int<T>::val) };
};
ลองออนไลน์!
boost::spirit
การแยกวิเคราะห์ มันเป็นมากกว่าสำหรับการแยกวิเคราะห์ไวยากรณ์ขอบคุณการแยกรูปแบบไฟล์ง่าย ๆ มีคนในทีมของฉันพยายามใช้เพื่อแยกวิเคราะห์ XML และเป็นปัญหาในการแก้ไขข้อบกพร่อง อยู่ห่างจากboost::spirit
ถ้าเป็นไปได้