[英]C++ file conversion: pipe delimited to comma delimited
我试图弄清楚如何将 pipe 分隔形式的输入文件转换为逗号分隔。 我必须打开文件,将其读入数组,将其转换为 output CSV 文件中的逗号分隔,然后关闭所有文件。 有人告诉我,最简单的方法是在 excel 内,但我不太确定如何。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile;
string myArray[5];
cout << "Enter the input filename:";
cin >> inFileName;
inFile.open(inFileName);
if(inFile.is_open())
std::cout<<"File Opened"<<std::endl;
// read file line by line into array
cout<<"Read";
for(int i = 0; i < 5; ++i)
{
file >> myArray[i];
}
// File conversion
// close input file
inFile.close();
// close output file
outFile.close();
...
我需要转换的是:
Miles per hour|6,445|being the "second" team |5.54|9.98|6,555.00
"Ending" game| left at "beginning"|Elizabeth, New Jersey|25.25|6.78|987.01
|End at night, or during the day|"Let's go"|65,978.21|0.00|123.45
Left-base night|10/07/1900|||4.07|777.23
"Let's start it"|Start Baseball Game|Starting the new game to win
output 应以逗号分隔的形式显示:
Miles per hour,"6,445","being the ""second"" team member",5.54,9.98,"6,555.00",
"""Ending"" game","left at ""beginning""","Denver, Colorado",25.25,6.78,987.01,
,"End at night, during the day","""Let's go""","65,978.21",0.00,123.45,
Left-base night, 10/07/1900,,,4.07,777.23,
"""Let's start it""", Start Baseball Game, Starting the new game to win,
我将向您展示一个完整的解决方案并向您解释。 但让我们先来看看它:
#include <iostream>
#include <vector>
#include <fstream>
#include <regex>
#include <string>
#include <algorithm>
// I omit in the example here the manual input of the filenames. This exercise can be done by somebody else
// Use fixed filenames in this example.
const std::string inputFileName("r:\\input.txt");
const std::string outputFileName("r:\\output.txt");
// The delimiter for the source csv file
std::regex re{ R"(\|)" };
std::string addQuotes(const std::string& s) {
// if there are single quotes in the string, then replace them with double quotes
std::string result = std::regex_replace(s, std::regex(R"(")"), R"("")");
// If there is any quote (") or comma in the file, then quote the complete string
if (std::any_of(result.begin(), result.end(), [](const char c) { return ((c == '\"') || (c == ',')); })) {
result = "\"" + result + "\"";
}
return result;
}
// Some output function
void printData(std::vector<std::vector<std::string>>& v, std::ostream& os) {
// Go throug all rows
std::for_each(v.begin(), v.end(), [&os](const std::vector<std::string>& vs) {
// Define delimiter
std::string delimiter{ "" };
// Show the delimited strings
for (const std::string& s : vs) {
os << delimiter << s;
delimiter = ",";
}
os << "\n";
});
}
int main() {
// We first open the ouput file, becuse, if this cannot be opened, then no meaning to do the rest of the exercise
// Open output file and check, if it could be opened
if (std::ofstream outputFileStream(outputFileName); outputFileStream) {
// Open the input file and check, if it could be opened
if (std::ifstream inputFileStream(inputFileName); inputFileStream) {
// In this variable we will store all lines from the CSV file including the splitted up columns
std::vector<std::vector<std::string>> data{};
// Now read all lines of the CSV file and split it into tokens
for (std::string line{}; std::getline(inputFileStream, line); ) {
// Split line into tokens and add to our resulting data vector
data.emplace_back(std::vector<std::string>(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}));
}
std::for_each(data.begin(), data.end(), [](std::vector<std::string>& vs) {
std::transform(vs.begin(), vs.end(), vs.begin(), addQuotes);
});
// Output, to file
printData(data, outputFileStream);
// And to the screen
printData(data, std::cout);
}
else {
std::cerr << "\n*** Error: could not open input file '" << inputFileName << "'\n";
}
}
else {
std::cerr << "\n*** Error: could not open output file '" << outputFileName << "'\n";
}
return 0;
}
那么,让我们来看看吧。 我们有 function
main
,读取csv文件,拆分成token,转换,写入addQuotes
。 必要时添加报价printData
打印他将数据转换为 output stream 让我们从main
开始。 main
将首先打开输入文件和 output 文件。
输入文件包含一种结构化数据,也称为 csv(逗号分隔值)。 但是这里我们没有逗号,而是一个 pipe 符号作为分隔符。
结果通常存储在二维向量中。 维度 1 是行,另一个维度是列。
那么,接下来我们需要做什么呢? 正如我们所看到的,我们首先需要阅读源代码 stream 中的所有完整文本行。 这可以通过单线轻松完成:
for (std::string line{}; std::getline(inputFileStream, line); ) {
正如您在此处看到的,for 语句有一个声明/初始化部分,然后是一个条件,然后是一个语句,在循环结束时执行。 这是众所周知的。
我们首先定义一个std::string
类型的变量“line”,并使用默认初始化器创建一个空字符串。 然后我们使用std::getline
从 stream 读取完整的一行并将其放入我们的变量中。 std::getline
返回对 sthe stream 的引用,并且 stream 有一个重载的 bool 运算符,如果出现故障(或文件结尾),它会返回。 因此,for 循环不需要额外检查文件结尾。 而且我们不使用 for 循环的最后一条语句,因为通过读取一行,文件指针会自动前进。
这给了我们一个非常简单的 for 循环,逐行读取一个完整的文件。
请注意:在 for 循环中定义变量“line”,将 scope 到 for 循环。 意思是,它只在 for 循环中可见。 这通常是防止外部名称空间污染的一个很好的解决方案。
好的,现在下一行:
data.emplace_back(std::vector<std::string>(std::sregex_token_iterator(line.begin(), line.end(), digit), {}));
哦哦,那是什么?
好的,让 go 一步一步来。 首先,我们显然想在我们的二维数据向量中添加一些东西。 我们将使用std::vector
s functionemplace_back 。 我们也可以使用push_back ,但这意味着我们需要对数据进行不必要的复制。 因此,我们选择emplace_back
来对我们想要添加到二维数据向量中的东西进行就地构造。
我们要添加什么? 我们想要添加一个完整的行,因此是一个列向量。 在我们的例子中是一个std::vector<std::string>
。 而且,因为我们想在原地构造这个向量,所以我们用向量范围构造函数来调用它。 请参阅此处:构造函数编号 5 。 range 构造函数接受 2 个迭代器,一个 begin 和一个 end 迭代器,作为参数,并将迭代器指向的所有值复制到向量中。
所以,我们期望一个开始和结束迭代器。 我们在这里看到了什么:
std::sregex_token_iterator(line.begin(), line.end(), digit)
{}
但这是什么东西, sregex_token_iterator ?
这是一个迭代器,它迭代一行中的模式。 模式由正则表达式给出。 您可以在此处阅读有关 C++ 正则表达式库的信息。 由于它非常强大,不幸的是,您需要了解它的时间更长一些。 我不能在这里覆盖它。 但让我们为我们的目的描述它的基本功能:您可以用某种元语言描述一个模式, std::sregex_token_iterator
将查找该模式,如果找到匹配项,则返回相关数据。 在我们的例子中,模式非常简单:数字。 这可以用“\d+”来描述,意味着尝试匹配一个或多个数字。
现在将{}
作为结束迭代器。 您可能已经读到{}
将执行默认构造/初始化。 如果您在这里阅读数字 1 ,那么您会看到“默认构造函数”构造了一个序列结束迭代器。 所以,正是我们需要的。
读取所有数据后,我们将单个字符串转换为所需的 output。 这将通过std::transform
和 function addQuotes
来完成。 这里的策略是先用双引号替换单引号。
然后,接下来,我们看一下,如果字符串中有任何逗号或引号,那么我们将整个字符串附加在引号中。
最后但同样重要的是,我们有一个简单的 output function 并将转换后的数据打印到文件中并在屏幕上打印。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.