簡體   English   中英

c++ 從文件到向量的字符串——更優雅的方式

[英]c++ String from file to vector - more elegant way

我寫了一個代碼,我想在其中將幾個字符串從文本文件傳遞到字符串向量。 目前我這樣做:

using namespace std;
int main()
{
string list_name="LIST";
ifstream REF;
REF.open(list_name.c_str());
vector<string> titles;
for(auto i=0;;i++)
{
    REF>>list_name;
    if(list_name=="-1"){break;}
    titles.push_back(list_name);
}
REF.close();
cout<<titles.size();
for(unsigned int i=0; i<titles.size(); i++)
{
    cout<<endl<<titles[i];
}

它工作正常,我按預期得到了 output。 我擔心的是,當將字符串從文件流傳遞到字符串 object 並將其分配給帶有 push_back 的向量作為單獨的步驟時,是否有更優雅的方法將字符串從文本文件直接傳遞到向量,從而避免此片段:

REF>>list_name;
if(list_name=="-1"){break;}
titles.push_back(list_name);

更優雅的算法方式

std::copy_if(std::istream_iterator<std::string>(REF),
    std::istream_iterator<std::string>(),
    std::back_inserter(titles),
    [](const std::string& t) { return t != "-1"; });

如果您事先知道要讀取的字符串數,則可以

using StringVector = std::vector<std::string>;
int main(int argc, const char* argv) {
  constexpr size_t N = 4; // or however many strings you want...
  StringVector data(N);
  std::ifstream stream("foo.txt");
  for (size_t i =0; (i < N) && stream; i++) {
    stream >> data[i];
  }
}

但這會不太靈活,並且實施"-1" “終止符”約定會更加棘手。

如果那個"-1"是一個真正的要求(與任意選擇相反),並且如果你多次使用它,它可能會得到“抽象”的回報,你如何閱讀這些字符串。 抽象通常以 function 的形式完成。

// compile with:
// clang++-13 -std=c++20 -g -O3 -o words words.cpp

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

using StringVector = std::vector<std::string>;

std::istream& operator>> (std::istream& stream, StringVector& sv)
{
  std::string word;
  while (stream) {
    stream >> word;
    if (word == "-1")
      return stream;
    sv.push_back(word);
  }
  return stream;
}

std::ostream& operator<< (std::ostream& stream,
              const StringVector& sv) {
  for (const auto& s : sv) {
    stream << s << std::endl;
  }
  return stream;
}

int main(int argc, const char* argv[]) {
  std::string file_data{R"(word1 word2 
word3
word4 -1)"};
  std::istringstream stream(file_data);

  StringVector data;
  data.reserve(10);
  stream >> data;
  std::cout
    << "Number of strings loaded: "
    << data.size() << std::endl;
  std::cout << data;
  
  return 0;
}

上面的operator>>()通常適用於流,因此它也適用於文件流。

順便說一句:人們不喜歡"-1"終結符方法的一個原因是性能。 如果你不斷地向一個 vector 推送任意次數,那么隨着 vector 的增長,需要重新分配 vector 的存儲,這是可以避免的開銷。 因此,通常人們會使用另一種文件格式,例如首先給出字符串的數量,然后給出字符串,這將允許:

size_t n;
stream >> n;
StringVector data;
data.reserve(n); // avoids "spurious reallocs as we load the strings"
for (size_t i = 0; i < n; i++) { ... }

其他答案可能太復雜或太復雜。

讓我先對您的代碼做一個小小的審查。 請在代碼中查看我的評論:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;  // You should not open the full std namespace. Better to use full qualifiacation
int main()
{
    string list_name = "LIST";
    ifstream REF;                  // Here you coud directly use the construct ofr the istream, which will open the file for you
    REF.open(list_name.c_str());   // No need to use c_str
    vector<string> titles;         // All variables should be initialized. Use {}
    for (auto i = 0;; i++)         // Endless loop. You could also write for(;;), but bad design
    {
        REF >> list_name;
        if (list_name == "-1") { break; }   // Break out of the endless loop. Bad design. Curly braces not needed
        titles.push_back(list_name);
    }
    REF.close();                   // No nbeed to close the file. With RAII, the destructor of the istream will close the file for you
    cout << titles.size();
    for (unsigned int i = 0; i < titles.size(); i++)  // Better to use a range based for loop 
    {
        cout << endl << titles[i];   // end not recommended. For cout`'\n' is beter, because it does not call flush unneccesarily.
    }
}

您會看到許多需要改進的地方。

讓我向您解釋一些更重要的主題。

  • 您應該使用std::ifstreams構造函數直接打開文件。
  • 始終檢查此類操作的結果。 bool! std::ifstream的運算符被覆蓋。 所以可以做一個簡單的測試
  • 不需要關閉文件。 std::ifstream的析構函數將為您完成該操作。
  • 有一個關於如何讀取文件的標准方法。 請看下面。

如果您想讀取文件直到 EOF(文件結尾)或任何其他條件,您可以簡單地使用 while 循環並調用提取運算符>>

例如:

while (REF >> list_name) {
    titles.push_back(list_name);
}

為什么這行得通? 提取運算符將始終返回對 stream 的引用及其調用。 因此,您可以想象在讀取字符串后,while 將包含while (REF) ,因為 REF 是由(REF >> list_name返回的。並且,如前所述,stream 的bool運算符被覆蓋並返回 state 的stream。如果有任何錯誤或 EOF,則if (REF)將為假。

所以現在附加條件:與“-1”的比較可以很容易地添加到 while 語句中。

while ((REF >> list_name) and (list_name != "-1")) {
    titles.push_back(list_name);
}

這是一個安全的操作,因為 boolean 快捷方式評估。 如果第一個條件已經為假,則不會評估第二個。

有了上面的所有知識,代碼可以重構為:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

int main() {

    // Here our source data is stored
    const std::string fileName{ "list.txt" };

    // Open the file and check, if it could be opened
    std::ifstream fileStream{ fileName };
    if (fileStream) {

        // Here we will store all titles that we read from the file
        std::vector<std::string> titles{};

        // Now read all data and store vit in our resulting vector
        std::string tempTitle{};
        while ((fileStream >> tempTitle) and (tempTitle != "-1"))
            titles.push_back(tempTitle);

        // For debug purposes. Show all titles on screen:
        for (const std::string title : titles)
            std::cout << '\n' << title;
    }
    else std::cerr << "\n*** Error: Could not open file '" << fileName << "'\n";
}

暫無
暫無

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

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