我需要在c++中加载和使用CSV文件数据。在这一点上,它实际上只是一个以逗号分隔的解析器(即不用担心转义新行和逗号)。主要需要的是逐行解析器,它将在每次调用方法时为下一行返回一个向量。
我发现这篇文章看起来很有前途: http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp
我从未使用过Boost's Spirit,但我愿意尝试一下。但前提是我忽略了一个更直接的解决方案。
我需要在c++中加载和使用CSV文件数据。在这一点上,它实际上只是一个以逗号分隔的解析器(即不用担心转义新行和逗号)。主要需要的是逐行解析器,它将在每次调用方法时为下一行返回一个向量。
我发现这篇文章看起来很有前途: http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp
我从未使用过Boost's Spirit,但我愿意尝试一下。但前提是我忽略了一个更直接的解决方案。
当前回答
可以使用std::regex。
根据文件大小和可用内存,可以逐行读取,也可以完全在std::string中读取。
读取文件可以使用:
std::ifstream t("file.txt");
std::string sin((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
然后你可以和这个相匹配,它实际上是根据你的需要定制的。
std::regex word_regex(",\\s]+");
auto what =
std::sregex_iterator(sin.begin(), sin.end(), word_regex);
auto wend = std::sregex_iterator();
std::vector<std::string> v;
for (;what!=wend ; wend) {
std::smatch match = *what;
v.push_back(match.str());
}
其他回答
您可以使用fopen,fscanf函数打开和读取.csv文件,但重要的是解析数据。使用分隔符解析数据的最简单方法。对于.csv,分隔符为','。
假设你的data1.csv文件如下所示:
A,45,76,01
B,77,67,02
C,63,76,03
D,65,44,04
您可以标记数据并存储在字符数组中,然后使用atoi()等函数进行适当的转换
FILE *fp;
char str1[10], str2[10], str3[10], str4[10];
fp = fopen("G:\\data1.csv", "r");
if(NULL == fp)
{
printf("\nError in opening file.");
return 0;
}
while(EOF != fscanf(fp, " %[^,], %[^,], %[^,], %s, %s, %s, %s ", str1, str2, str3, str4))
{
printf("\n%s %s %s %s", str1, str2, str3, str4);
}
fclose(fp);
[^,], ^ -它颠倒了逻辑,意思是匹配任何不包含逗号的字符串,然后最后,表示匹配终止前一个字符串的逗号。
就像每个人都把他的解决方案,这里是我的使用模板,lambda和tuple。
它可以将任何具有所需列的CSV转换为tuple的c++向量。
它通过在元组中定义每个CSV行元素类型来工作。
您还需要为每个元素定义std::string到类型转换Formatter lambda(例如使用std::atod)。
然后你就得到了这个结构的一个向量,对应于你的CSV数据。
您可以很容易地重用它来匹配任何CSV结构。
StringsHelpers.hpp
#include <string>
#include <fstream>
#include <vector>
#include <functional>
namespace StringHelpers
{
template<typename Tuple>
using Formatter = std::function<Tuple(const std::vector<std::string> &)>;
std::vector<std::string> split(const std::string &string, const std::string &delimiter);
template<typename Tuple>
std::vector<Tuple> readCsv(const std::string &path, const std::string &delimiter, Formatter<Tuple> formatter);
};
StringsHelpers.cpp
#include "StringHelpers.hpp"
namespace StringHelpers
{
/**
* Split a string with the given delimiter into several strings
*
* @param string - The string to extract the substrings from
* @param delimiter - The substrings delimiter
*
* @return The substrings
*/
std::vector<std::string> split(const std::string &string, const std::string &delimiter)
{
std::vector<std::string> result;
size_t last = 0,
next = 0;
while ((next = string.find(delimiter, last)) != std::string::npos) {
result.emplace_back(string.substr(last, next - last));
last = next + 1;
}
result.emplace_back(string.substr(last));
return result;
}
/**
* Read a CSV file and store its values into the given structure (Tuple with Formatter constructor)
*
* @tparam Tuple - The CSV line structure format
*
* @param path - The CSV file path
* @param delimiter - The CSV values delimiter
* @param formatter - The CSV values formatter that take a vector of strings in input and return a Tuple
*
* @return The CSV as vector of Tuple
*/
template<typename Tuple>
std::vector<Tuple> readCsv(const std::string &path, const std::string &delimiter, Formatter<Tuple> formatter)
{
std::ifstream file(path, std::ifstream::in);
std::string line;
std::vector<Tuple> result;
if (file.fail()) {
throw std::runtime_error("The file " + path + " could not be opened");
}
while (std::getline(file, line)) {
result.emplace_back(formatter(split(line, delimiter)));
}
file.close();
return result;
}
// Forward template declarations
template std::vector<std::tuple<double, double, double>> readCsv<std::tuple<double, double, double>>(const std::string &, const std::string &, Formatter<std::tuple<double, double, double>>);
} // End of StringHelpers namespace
Main.cpp(一些用法)
#include "StringHelpers.hpp"
/**
* Example of use with a CSV file which have (number,Red,Green,Blue) as line values. We do not want to use the 1st value
* of the line.
*/
int main(int argc, char **argv)
{
// Declare CSV line type, formatter and template type
typedef std::tuple<double, double, double> CSV_format;
typedef std::function<CSV_format(const std::vector<std::string> &)> formatterT;
enum RGB { Red = 1, Green, Blue };
const std::string COLOR_MAP_PATH = "/some/absolute/path";
// Load the color map
auto colorMap = StringHelpers::readCsv<CSV_format>(COLOR_MAP_PATH, ",", [](const std::vector<std::string> &values) {
return CSV_format {
// Here is the formatter lambda that convert each value from string to what you want
std::strtod(values[Red].c_str(), nullptr),
std::strtod(values[Green].c_str(), nullptr),
std::strtod(values[Blue].c_str(), nullptr)
};
});
// Use your colorMap as you wish...
}
如果你确实关心正确解析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);
}
我写了一个很好的解析CSV文件的方法,我认为我应该把它作为一个答案:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
struct CSVDict
{
std::vector< std::string > inputImages;
std::vector< double > inputLabels;
};
/**
\brief Splits the string
\param str String to split
\param delim Delimiter on the basis of which splitting is to be done
\return results Output in the form of vector of strings
*/
std::vector<std::string> stringSplit( const std::string &str, const std::string &delim )
{
std::vector<std::string> results;
for (size_t i = 0; i < str.length(); i++)
{
std::string tempString = "";
while ((str[i] != *delim.c_str()) && (i < str.length()))
{
tempString += str[i];
i++;
}
results.push_back(tempString);
}
return results;
}
/**
\brief Parse the supplied CSV File and obtain Row and Column information.
Assumptions:
1. Header information is in first row
2. Delimiters are only used to differentiate cell members
\param csvFileName The full path of the file to parse
\param inputColumns The string of input columns which contain the data to be used for further processing
\param inputLabels The string of input labels based on which further processing is to be done
\param delim The delimiters used in inputColumns and inputLabels
\return Vector of Vector of strings: Collection of rows and columns
*/
std::vector< CSVDict > parseCSVFile( const std::string &csvFileName, const std::string &inputColumns, const std::string &inputLabels, const std::string &delim )
{
std::vector< CSVDict > return_CSVDict;
std::vector< std::string > inputColumnsVec = stringSplit(inputColumns, delim), inputLabelsVec = stringSplit(inputLabels, delim);
std::vector< std::vector< std::string > > returnVector;
std::ifstream inFile(csvFileName.c_str());
int row = 0;
std::vector< size_t > inputColumnIndeces, inputLabelIndeces;
for (std::string line; std::getline(inFile, line, '\n');)
{
CSVDict tempDict;
std::vector< std::string > rowVec;
line.erase(std::remove(line.begin(), line.end(), '"'), line.end());
rowVec = stringSplit(line, delim);
// for the first row, record the indeces of the inputColumns and inputLabels
if (row == 0)
{
for (size_t i = 0; i < rowVec.size(); i++)
{
for (size_t j = 0; j < inputColumnsVec.size(); j++)
{
if (rowVec[i] == inputColumnsVec[j])
{
inputColumnIndeces.push_back(i);
}
}
for (size_t j = 0; j < inputLabelsVec.size(); j++)
{
if (rowVec[i] == inputLabelsVec[j])
{
inputLabelIndeces.push_back(i);
}
}
}
}
else
{
for (size_t i = 0; i < inputColumnIndeces.size(); i++)
{
tempDict.inputImages.push_back(rowVec[inputColumnIndeces[i]]);
}
for (size_t i = 0; i < inputLabelIndeces.size(); i++)
{
double test = std::atof(rowVec[inputLabelIndeces[i]].c_str());
tempDict.inputLabels.push_back(std::atof(rowVec[inputLabelIndeces[i]].c_str()));
}
return_CSVDict.push_back(tempDict);
}
row++;
}
return return_CSVDict;
}
使用Spirit来解析csv并不过分。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的各种技术。生成的代码与手写代码一样紧凑。检查生成的汇编程序!