我在 CSV 檔案中有一百萬個條目,需要加載它。但是,完成加載大約需要 2 分鐘。我需要解決這個問題以加快資料加載速度。有沒有更好的方法可以弄清楚?所以我可以研究并嘗試修復它。感謝您的任何幫助。
CSVReader.h
#pragma once
#include "OrderBookEntry.h"
#include <vector>
#include <string>
class CSVReader
{
public:
CSVReader();
static std::vector<OrderBookEntry> readCSV (std::string csvFile);
};
CSVReader.cpp
#include "CSVReader.h"
#include <iostream>
#include <fstream>
CSVReader::CSVReader() {
}
std::vector<OrderBookEntry> CSVReader::readCSV(std::string csvFilename) {
std::vector<OrderBookEntry> entries;
std::ifstream csvFile{csvFilename};
std::string line;
if (csvFile.is_open())
{
while (std::getline(csvFile, line))
{
try {
OrderBookEntry obe = stringsToOBE(tokenise(line, ','));
entries.push_back(obe);
}
catch(const std::exception& e){
std::cout << "CSVReader::readCSV bad data" << std::endl;
}
}//end of while
}
std::cout << "Successfully read " << entries.size() << " entries" << std::endl;
return entries;
}
std::vector<std::string> CSVReader::tokenise(std::string csvLine, char separator) {
std::vector<std::string>tokens;
signed int start, end;
std::string token;
start = csvLine.find_first_not_of(separator, 0);
do {
end = csvLine.find_first_of(separator, start);
if (start == csvLine.length() || start == end) break;
if (end >= 0) token = csvLine.substr(start, end - start);
else token = csvLine.substr(start, csvLine.length() - start);
tokens.push_back(token);
start = end 1;
} while (end > 0);
return tokens;
}
OrderBookEntry CSVReader::stringsToOBE(std::vector<std::string>tokens) {
double price, amount;
if (tokens.size() != 5) {
std::cout << "Bad Input" << std::endl;
throw std::exception{};
}
try {
//we have 5 tokens
price = std::stod(tokens[3]);
amount = std::stod(tokens[4]);
}
catch(const std::exception& e){
std::cout << "Bad Float!" << tokens[3] << std::endl;
std::cout << "Bad Float!" << tokens[4] << std::endl;
throw;
}
OrderBookEntry
obe{price,amount,tokens[0],tokens[1],OrderBookEntry::stringToOrderBookType(tokens[2])};
return obe;
}
uj5u.com熱心網友回復:
正如評論中提到的,應該優化的第一件事是按值傳遞引數。即,應該有const std::string& arg_name代替std::string arg_name等。
那么,std::vector使用也不是那么理想。這是因為vector的性質:它連續存盤其元素。這意味著如果在某個時候沒有記憶體以這種方式(連續)附加下一個元素,那么所有現有元素都應該重新分配到新位置。因此,如果vector使用不是非常需要的,請考慮用std::list其他東西代替它。
然后,我會CSVReader::tokenise像這樣簡化方法:
std::list<std::string> CSVReader::tokenise(const std::string& str, char sep)
{
std::list<string> result;
std::string::size_type cur = 0;
for(auto pos = str.find_first_of(sep); pos != string::npos; cur = pos 1, pos = str.find_first_of(sep, cur))
result.emplace_back(str, cur, pos - cur);
result.emplace_back(str, cur);
return result;
}
然后,我將結合tokenise和stringsToOBE方法。
通常,查看回圈中執行的代碼。
uj5u.com熱心網友回復:
想想你在決議一行時正在做什么樣的操作,以及效率低下的原因。
例如,
std::vector<std::string> CSVReader::tokenise(std::string csvLine, char separator)
它csvLine通過副本接受。復制字串是一個潛在的緩慢操作,因為它可能涉及記憶體分配。
然后你從一個字串創建一個字串向量......創建一個向量不是更好std::string_view嗎?所以你不要復制字串的每個元素。為了使函式更安全,需要標記化接受 a std::string_viewas well 而不是std::stringorconst std::string&
接下來,通過查看CSVReader::stringsToOBE(std::vector<std::string>tokens),顯然我看到您實際上需要大小為 5。然后將一些元素決議為浮點數并將一些元素復制為字串,另一個轉換為列舉。
要求輸入為 不是更有意義std::array<std::string_view,5>嗎?編譯時強制大小為 5 而不是測驗它?為了適應這種變化,您可以制作tokenize一個模板,該模板接受一個數字n作為模板引數,并根據分隔符將 astd::string_view轉換std::array<std::string_view,n>為,最后驗證它是否是字串視圖的結尾。
由于消除了不必要的std::vector.
但是為什么要從中獲取一個字串ifstream,然后決議它并分別轉換每個元素呢?
為什么不直接從流中提取元素而不創建中間字串?說,添加功能OrderBookEntry CSVReader::extractOBEElement(std::istream& stream)?只需使用流的本機功能,例如>>?
它可能有效并且效率更高,但眾所周知,流很慢,因此首先生成字串可能確實更可取。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/397997.html
