C++14
一般来说,大学的工作人员建议我们使用Boost来解析文件,但我已经安装了它,但没有成功地用它实现任何东西。
所以我必须逐行解析 CSV 文件,其中每行有 2 列,当然用逗号分隔。这两列中的每一列都是一个数字。我必须取这两个数字的整数值并使用它们最后构造我的分形对象。
第一个问题是:该文件可能如下所示:
1,1
<HERE WE HAVE A NEWLINE>
<HERE WE HAVE A NEWLINE>
Run Code Online (Sandbox Code Playgroud)
这种格式的文件没问题。但我的解决方案输出“无效输入”,其中正确的解决方案应该只打印一次相应的分形 - 1,1。
第二个问题是:该文件可能如下所示:
1,1
<HERE WE HAVE A NEWLINE>
1,1
Run Code Online (Sandbox Code Playgroud)
这应该是一个无效的输入,但我的解决方案将其视为正确的输入 - 并且只是跳过中间的换行符。
也许你可以指导我如何解决这些问题,这对我来说真的很有帮助,因为我从早到晚都在努力进行这项练习。
这是我当前的解析器:
#include <iostream>
#include "Fractal.h"
#include <fstream>
#include <stack>
#include <sstream>
const char *usgErr = "Usage: FractalDrawer <file path>\n";
const char *invalidErr = "Invalid input\n";
const char *VALIDEXT = "csv";
const char EXTDOT = '.';
const char COMMA = ',';
const char MINTYPE = 1;
const char MAXTYPE = 3;
const int MINDIM = 1;
const int MAXDIM = 6;
const int NUBEROFARGS = 2;
int main(int argc, char *argv[])
{
if (argc != NUBEROFARGS)
{
std::cerr << usgErr;
std::exit(EXIT_FAILURE);
}
std::stack<Fractal *> resToPrint;
std::string filepath = argv[1]; // Can be a relative/absolute path
if (filepath.substr(filepath.find_last_of(EXTDOT) + 1) != VALIDEXT)
{
std::cerr << invalidErr;
exit(EXIT_FAILURE);
}
std::stringstream ss; // Treat it as a buffer to parse each line
std::string s; // Use it with 'ss' to convert char digit to int
std::ifstream myFile; // Declare on a pointer to file
myFile.open(filepath); // Open CSV file
if (!myFile) // If failed to open the file
{
std::cerr << invalidErr;
exit(EXIT_FAILURE);
}
int type = 0;
int dim = 0;
while (myFile.peek() != EOF)
{
getline(myFile, s, COMMA); // Read to comma - the kind of fractal, store it in s
ss << s << WHITESPACE; // Save the number in ss delimited by ' ' to be able to perform the double assignment
s.clear(); // We don't want to save this number in s anymore as we won't it to be assigned somewhere else
getline(myFile, s, NEWLINE); // Read to NEWLINE - the dim of the fractal
ss << s;
ss >> type >> dim; // Double assignment
s.clear(); // We don't want to save this number in s anymore as we won't it to be assigned somewhere else
if (ss.peek() != EOF || type < MINTYPE || type > MAXTYPE || dim < MINDIM || dim > MAXDIM)
{
std::cerr << invalidErr;
std::exit(EXIT_FAILURE);
}
resToPrint.push(FractalFactory::factoryMethod(type, dim));
ss.clear(); // Clear the buffer to update new values of the next line at the next iteration
}
while (!resToPrint.empty())
{
std::cout << *(resToPrint.top()) << std::endl;
resToPrint.pop();
}
myFile.close();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我不会更新你的代码。我看了你的标题Parsing a CSV file - C++,想向你展示如何以更现代的方式读取 csv 文件。不幸的是你仍然使用 C++14。getlines使用 C++20 或范围库,使用和将会非常简单split。
在 C++17 中,我们可以使用 CTAD 和if初始化程序等等。
但我们不需要的是 boost。C++ 的标准库就足够了。我们从不使用scanf这样的旧东西。
以我的拙见,链接到 10 年前的问题How can I read and parse CSV files in C++? 不应再给予。现在已经是2020年了。应该使用更现代和现在可用的语言元素。但正如所说。每个人都可以自由地做他想做的事。
在C++中我们可以使用std::sregex_token_iterator. 而且它的使用方法非常简单。它也不会显着减慢你的程序速度。双倍std::getline也可以。虽然不是那么灵活。为此必须知道列数。这std::sregex_token_iterator关心列数。
请参阅以下示例代码。在此,我们创建一个代理类并覆盖其提取器运算符。然后我们用std::istream_iterator一小行代码读取并解析整个 csv 文件。
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <regex>
#include <string>
#include <vector>
// Define Alias for easier Reading
// using Columns = std::vector<std::string>;
using Columns = std::vector<int>;
// The delimiter
const std::regex re(",");
// Proxy for the input Iterator
struct ColumnProxy {
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {
// Read a line
std::string line;
cp.columns.clear();
if(std::getline(is, line) && !line.empty()) {
// Split values and copy into resulting vector
std::transform(
std::sregex_token_iterator(line.begin(), line.end(), re, -1), {},
std::back_inserter(cp.columns),
[](const std::string& s) { return std::stoi(s); });
}
return is;
}
// Type cast operator overload. Cast the type 'Columns' to
// std::vector<std::string>
operator Columns() const { return columns; }
protected:
// Temporary to hold the read vector
Columns columns{};
};
int main() {
std::ifstream myFile("r:\\log.txt");
if(myFile) {
// Read the complete file and parse verything and store result into vector
std::vector<Columns> values(std::istream_iterator<ColumnProxy>(myFile), {});
// Show complete csv data
std::for_each(values.begin(), values.end(), [](const Columns& c) {
std::copy(c.begin(), c.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
});
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
请注意:还有大量其他可能的解决方案。请随意使用您想要的任何东西。
编辑
因为我在这里看到很多复杂的代码,所以我想展示第二个示例来说明如何
解析 CSV 文件 - C++
基本上,代码中不需要超过 2 条语句。您首先定义数字的正则表达式。然后,您使用 C++ 语言元素,该元素专为将字符串标记为子字符串而设计。这std::sregex_token_iterator。而且由于多年来 C++ 中已经提供了这样一个最合适的语言元素,因此可能值得考虑使用它。也许您基本上可以用 2 行而不是 10 行或更多行来完成该任务。而且很容易理解。
当然,有数千种可能的解决方案,有些喜欢继续 C 风格,而另一些则喜欢更现代的 C++ 功能。这取决于每个人个人的决定。
下面的代码按照指定读取 csv 文件,无论它包含多少行(行)以及每行有多少列。即使是外国角色也可以在其中。空行将是 csv 向量中的空条目。这也可以很容易地避免,在 emplace 返回之前使用“if !empty”。
但有些人喜欢这样,另一些人也喜欢这样。人们想要什么就什么。
请看一个一般示例:
#include <algorithm>
#include <iterator>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
// Test data. Can of course also be taken from a file stream.
std::stringstream testFile{ R"(1,2
3, a, 4
5 , 6 b , 7
abc def
8 , 9
11 12 13 14 15 16 17)" };
std::regex digits{R"((\d+))"};
using Row = std::vector<std::string>;
int main() {
// Here we will store all the data from the CSV as std::vector<std::vector<std::string>>
std::vector<Row> csv{};
// This extremely simple 2 lines will read the complete CSV and parse the data
for (std::string line{}; std::getline(testFile, line); )
csv.emplace_back(Row(std::sregex_token_iterator(line.begin(), line.end(), digits, 1), {}));
// Now, you can do with the data, whatever you want. For example: Print double the value
std::for_each(csv.begin(), csv.end(), [](const Row& r) {
if (!r.empty()) {
std::transform(r.begin(), r.end(), std::ostream_iterator<int>(std::cout, " "), [](const std::string& s) {
return std::stoi(s) * 2; }
); std::cout << "\n";}});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以,现在,你可能明白了,你可能喜欢它,或者你不喜欢它。任何。随意做任何你想做的事。
| 归档时间: |
|
| 查看次数: |
6978 次 |
| 最近记录: |