[英]C++: reading in from a text file
我試圖找出讀取 a.txt 文件的最佳方法,用逗號分隔信息,行分隔。 使用該信息創建我的股票 class 的對象。
.txt 文件如下所示:
GOOGL, 938.85, 30
APPL, 506.34, 80
MISE, 68.00, 300
我的股票 class 構造函數就像 stock(string symbol, double price, int numOfShares);
在我的主程序中,我想設置一個輸入文件 stream,它將讀取信息並創建股票 class 的對象,如下所示:
stock stock1("GOOGL", 938.85, 30);
stock stock2("APPL", 380.50, 60);
我假設我使用 ifstream 和 getline,但不太確定如何設置它。
謝謝!
#include <fstream>
#include <string>
#include <sstream>
int main()
{
//Open file
std::ifstream file("C:\\Temp\\example.txt");
//Read each line
std::string line;
while (std::getline(file, line))
{
std::stringstream ss(line);
std::string symbol;
std::string numstr;
//Read each comma delimited string and convert to required type
std::getline(ss, symbol, ',');
std::getline(ss, numstr, ',');
double price = std::stod(numstr);
std::getline(ss, numstr, ',');
int numOfShares = std::stoi(numstr);
//Construct stock object with variables above
stock mystock(symbol, price, numOfShares);
}
return 0;
}
似乎您想閱讀 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
用於在輸入字符串中應該匹配/不應該匹配的內容。 匹配策略的類型由最后一個參數給出。
所以,既然我們理解了迭代器,我們就可以將迭代器中的標記 std::copy 復制到我們的目標,即std::string
的std::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 comma
-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::cin
或std::ifstream
或std::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"(GOOGL, 938.85, 30
APPL, 506.34, 80
MISE, 68.00, 300)" };
// 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.