简体   繁体   English

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

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

I write a code in which I want to pass several strings from text file to string vector.我写了一个代码,我想在其中将几个字符串从文本文件传递到字符串向量。 Currently I do this that 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];
}

It works fine, I get the output as expected.它工作正常,我按预期得到了 output。 My concern is is there more elegant way to pass string from text file to vector directly, avoiding this fragment, when passing string from filestream to string object and assigning it to the vector with push_back as separate step:我担心的是,当将字符串从文件流传递到字符串 object 并将其分配给带有 push_back 的向量作为单独的步骤时,是否有更优雅的方法将字符串从文本文件直接传递到向量,从而避免此片段:

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

More elegant way with algorithms更优雅的算法方式

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"; });

If you knew the number of strings to read beforehand, you could如果您事先知道要读取的字符串数,则可以

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];
  }
}

But this would be less flexible and it would be trickier to implement your "-1" "terminator" convention.但这会不太灵活,并且实施"-1" “终止符”约定会更加棘手。

If that "-1" thing is a true requirement (in contrast to an arbitrary choice), and if you use this more than once, it might pay off to "abstract", how you read those strings.如果那个"-1"是一个真正的要求(与任意选择相反),并且如果你多次使用它,它可能会得到“抽象”的回报,你如何阅读这些字符串。 Abstraction is usually done in form of a function.抽象通常以 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;
}

The above operator>>() works for streams in general, so it also works for file streams.上面的operator>>()通常适用于流,因此它也适用于文件流。

As an aside: One reason, why people would not like the "-1" terminator approach is performance.顺便说一句:人们不喜欢"-1"终结符方法的一个原因是性能。 If you keep pushing into a vector an arbitrary amount of times, the storage of the vector needs to be re-allocated as the vector grows, which is avoidable overhead.如果你不断地向一个 vector 推送任意次数,那么随着 vector 的增长,需要重新分配 vector 的存储,这是可以避免的开销。 So, usually people would use another file format, eg giving the number of strings first, then the strings, which would allow for:因此,通常人们会使用另一种文件格式,例如首先给出字符串的数量,然后给出字符串,这将允许:

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++) { ... }

The other answers are maybe too complicated or too complex.其他答案可能太复杂或太复杂。

Let me first do a small review of your code.让我先对您的代码做一个小小的审查。 Please see my comments within the code:请在代码中查看我的评论:

#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.
    }
}

You see many points for improvement.您会看到许多需要改进的地方。

Let me explain some of the more important topics to you.让我向您解释一些更重要的主题。

  • You should use the std::ifstreams constructor to directly open the file.您应该使用std::ifstreams构造函数直接打开文件。
  • Always check the result of such an operation.始终检查此类操作的结果。 The bool and ! bool! operator for the std::ifstream are overwritten. std::ifstream的运算符被覆盖。 So a simple test can be done所以可以做一个简单的测试
  • Not need to close the file.不需要关闭文件。 The Destructor of the std::ifstream will do that for you. std::ifstream的析构函数将为您完成该操作。
  • There is a standard approach on how to read a file.有一个关于如何读取文件的标准方法。 Please see below.请看下面。

If you want to read file until EOF (end of file) or any other condition, you can simply use a while loop and call the extraction operator >>如果您想读取文件直到 EOF(文件结尾)或任何其他条件,您可以简单地使用 while 循环并调用提取运算符>>

For example:例如:

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

Why does this work?为什么这行得通? The extraction operator will always return a reference to the stream with what it was called.提取运算符将始终返回对 stream 的引用及其调用。 So, you can imagine that after reading the string, the while would contain while (REF) , because REF was returned by (REF >> list_name . And, as mentioned already, the bool operator of the stream is overwritten and returns the state of the stream. If there would be any error or EOF, then if (REF) would be false.因此,您可以想象在读取字符串后,while 将包含while (REF) ,因为 REF 是由(REF >> list_name返回的。并且,如前所述,stream 的bool运算符被覆盖并返回 state 的stream。如果有任何错误或 EOF,则if (REF)将为假。

So and now the additional condition: A comparison with "-1" can be easily added to the while statement.所以现在附加条件:与“-1”的比较可以很容易地添加到 while 语句中。

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

This is a safe operatrion, because of boolean short-cut evaluation.这是一个安全的操作,因为 boolean 快捷方式评估。 If the first condition is already false, the second will not be evaluated.如果第一个条件已经为假,则不会评估第二个。

With all the knwo-how above, the code could be refactored to:有了上面的所有知识,代码可以重构为:

#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.

相关问题 C ++:将字符串和内容分割成std :: vector的优雅方式 - C++: Elegant way to split string and stuff contents into std::vector 在C ++中有没有更优雅的方式将sprintf和std :: string结合起来? - Is there a more elegant way of marrying sprintf and std::string in C++? 有没有更有效的方法来存储向量<vector<string> &gt;? C++ </vector<string> - Is there a more efficient way for storing vector<vector<string>> ? C++ 从文件c ++加载原始字符串的更简洁方法 - More concise way to load a raw string from a file c++ 查找 class 实例是否包含在 std::vector (C++) 中的优雅方法 - Elegant way to find if a class instance is contained in a std::vector (C++) 在C ++中连接C字符串的更优雅的方法? - A more elegant way to concatenate C strings in C++? 更优雅的方法来检查 C++ 数组中的重复项? - More elegant way to check for duplicates in C++ array? 在 C++ 中获取当前年份的更优雅的方式 - More elegant way to get the current year in C++ 有没有更优雅的方法来实现C ++游戏的“作弊代码”实现? - Is there a more elegant way to achieve a “cheat code” implementation for a game in C++? 转换矢量 <double> 矢量 <string> (优雅的方式) - Convert vector<double> to vector<string> ( elegant way )
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM