簡體   English   中英

從文件中逐字讀取

[英]Read word by word from a file

我有一個 class 是 Food 和 its.h 是:

class Food{

private:

string name;

int protein;

int calories;

int fats;

string type;

public:

....

friend istream & operator >> (istream &i, Food & other);

};

因此,使用運算符 >> 我想從具有以下格式的文件中讀取一種食物的屬性:

Ketchup;98;24;2;Processed

我試着做:

istream & operator >> (istream &i, Food & other){
    string nom, t;

    int c, p, h, g, f;

    char aux;   //Aux stores ';'

    i >> nom >> aux >> c >> aux >> p >> aux >> f;

    getline(i,t); //I read the type and change the line that I'm reading

    Food ing(nom.c_str(),c,p,f,t.c_str()); //Constructor

    other = ing;  //Overload of operator =

    return i;
}

但我沒有成功,請幫助?

似乎您想閱讀 csv 數據。 這是一個標准任務,我會給你詳細的解釋。 最后,所有的閱讀都將在一行中完成。

我建議使用“現代” C++ 方法。

仍然所有談論 csv 的人都在鏈接到How can I read and parse CSV files in C++? ,問題是從 2009 年開始,現在已經 10 多年了。 大多數答案也很舊且非常復雜。 所以,也許是時候改變了。

在現代 C++ 中,您擁有迭代范圍的算法。 你會經常看到類似“someAlgoritm(container.begin(), container.end(), someLambda)”的東西。 這個想法是我們迭代一些相似的元素。

在您的情況下,我們遍歷輸入字符串中的標記,並創建子字符串。 這稱為標記化。

正是為了這個目的,我們有std::sregex_token_iterator 而且因為我們有為此目的而定義的東西,我們應該使用它。

這東西是一個迭代器。 用於迭代字符串,因此是正則表達式。 開始部分定義了我們將操作的輸入范圍,然后有一個std::regex用於在輸入字符串中應該匹配/不應該匹配的內容。 匹配策略的類型由最后一個參數給出。

  • 1 --> 給我我在正則表達式中定義的東西和
  • -1 --> 告訴我根據正則表達式不匹配的內容。

所以,既然我們理解了迭代器,我們就可以將迭代器中的標記 std::copy 復制到我們的目標,即std::stringstd::vector 由於我們不知道我們有多少列,我們將使用std::back_inserter作為目標。 這會將我們從std::sregex_token_iterator和 append 獲得的所有令牌添加到我們的std::vector<std::string>>中。 我們有多少列並不重要。

好的。 這樣的聲明可能看起來像

std::copy(                          // We want to copy something
    std::sregex_token_iterator      // The iterator begin, the sregex_token_iterator. Give back first token
    (
        line.begin(),               // Evaluate the input string from the beginning
        line.end(),                 // to the end
        re,                         // Add match a semicolon
        -1                          // But give me back not the comma but everything else 
    ),
    std::sregex_token_iterator(),   // iterator end for sregex_token_iterator, last token + 1
    std::back_inserter(cp.columns)  // Append everything to the target container
);

現在我們可以理解,這個復制操作是如何工作的。

下一步。 我們想從文件中讀取。 該文件還包含某種相同的數據。 相同的數據是行。

如上所述,我們可以迭代相似的數據。 如果是文件輸入或其他。 為此, C++ 具有std::istream_iterator 這是一個模板,作為模板參數,它獲取應讀取的數據類型,作為構造函數參數,它獲取對輸入 stream 的引用。 沒關系,如果輸入 stream 是std::cinstd::ifstreamstd::istringstream 所有類型的流的行為都是相同的。

由於我們沒有 SO 文件,因此我使用(在下面的示例中) std::istringstream來存儲輸入 csv 文件。 當然,您可以通過定義std::ifstream testCsv(filename)打開文件。 沒問題。

使用std::istream_iterator ,我們遍歷輸入並讀取相似的數據。 在我們的例子中,一個問題是我們想要迭代特殊數據而不是一些內置數據類型。

為了解決這個問題,我們定義了一個代理 class,它為我們做內部工作(我們不想知道如何,應該封裝在代理中)。 在代理中,我們覆蓋了類型轉換運算符,以將結果變為我們預期的std::istream_iterator類型。

最后一個重要的步驟。 std::vector有一個范圍構造函數。 它還有很多其他構造函數,我們可以在std::vector類型的變量的定義中使用它們。 但是對於我們的目的,這個構造函數最適合。

所以我們定義了一個變量 csv 並使用它的范圍構造函數並給它一個范圍的開始和一個范圍的結束。 而且,在我們的具體示例中,我們使用std::istream_iterator的開始和結束迭代器。

如果我們結合以上所有內容,閱讀完整的 CSV 文件是單行的,它是調用其構造函數的變量的定義。

請查看生成的代碼:

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>

std::istringstream testCsv{ R"(Ketchup;98;24;2;Processed
Fries;10;20;2;Processed
Meat;10;20;2;Processed)" };


// Define Alias for easier Reading
using Columns = std::vector<std::string>;
using CSV = std::vector<Columns>;


// 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();
        std::getline(is, line);

        // The delimiter
        const std::regex re(";");

        // Split values and copy into resulting vector
        std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
            std::sregex_token_iterator(),
            std::back_inserter(cp.columns));
        return is;
    }

    // Type cast operator overload.  Cast the type 'Columns' to std::vector<std::string>
    operator std::vector<std::string>() const { return columns; }
protected:
    // Temporary to hold the read vector
    Columns columns{};
};


int main()
{
    // Define variable CSV with its range constructor. Read complete CSV in this statement, So, one liner
    CSV csv{ std::istream_iterator<ColumnProxy>(testCsv), std::istream_iterator<ColumnProxy>() };

    // Print result. Go through all lines and then copy line elements to std::cout
    std::for_each(csv.begin(), csv.end(), [](Columns& c) {
        std::copy(c.begin(), c.end(), std::ostream_iterator<std::string>(std::cout, " ")); std::cout << "\n";   });
}

我希望解釋足夠詳細,可以讓您了解現代 C++ 可以做什么。

這個例子基本上不關心源文本文件中有多少行和列。 它會吃掉一切。

當然,您可以將字符串轉換為您需要的任何內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM