繁体   English   中英

C++文件转换:pipe分隔为逗号分隔

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM