从文件到向量的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];
}
它工作正常,我得到了预期的输出。我担心的是,当将字符串从文件流传递到字符串对象并将其分配给向量时,是否有更优雅的方法直接将字符串从文本文件传递到向量,从而避免此片段: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"
是真正的要求(与任意选择相反),并且如果您不止一次使用它,它可能会对您阅读这些字符串的方式“抽象化”。抽象通常以函数的形式完成。
// 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"
终止符方法的一个原因是性能。如果你不断地向一个向量中推送任意次数,那么随着向量的增长,向量的存储需要 re-allocated,这是可以避免的开销。所以,通常人们会使用另一种文件格式,例如首先给出字符串的数量,然后是字符串,这将允许:
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
构造函数直接打开文件。
- 始终检查此类操作的结果。
std::ifstream
的 bool
和 !
运算符被覆盖。所以可以做个简单的测试
- 不需要关闭文件。
std::ifstream
的析构函数将为您完成。
- 有一个关于如何读取文件的标准方法。请看下面。
如果你想读取文件直到 EOF(文件结束)或任何其他条件,你可以简单地使用 while 循环并调用提取运算符 >>
例如:
while (REF >> list_name) {
titles.push_back(list_name);
}
为什么这行得通?提取运算符将始终 return 对流的引用以及它所调用的内容。因此,您可以想象在读取字符串后,while 将包含 while (REF)
,因为 REF 是由 (REF >> list_name
编辑的 return。并且,如前所述,流的 bool
运算符被覆盖并且 return 是流的状态。如果有任何错误或 EOF,则 if (REF)
将为假。
现在附加条件:与“-1”的比较可以很容易地添加到 while 语句中。
while ((REF >> list_name) and (list_name != "-1")) {
titles.push_back(list_name);
}
这是一个安全的操作,因为布尔值 short-cut 评估。如果第一个条件已经为假,则不会评估第二个。
对于上面的所有 knwo-how,代码可以重构为:
#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";
}
我写了一段代码,我想在其中将几个字符串从文本文件传递到字符串向量。目前我是这样做的:
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];
}
它工作正常,我得到了预期的输出。我担心的是,当将字符串从文件流传递到字符串对象并将其分配给向量时,是否有更优雅的方法直接将字符串从文本文件传递到向量,从而避免此片段: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"
是真正的要求(与任意选择相反),并且如果您不止一次使用它,它可能会对您阅读这些字符串的方式“抽象化”。抽象通常以函数的形式完成。
// 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"
终止符方法的一个原因是性能。如果你不断地向一个向量中推送任意次数,那么随着向量的增长,向量的存储需要 re-allocated,这是可以避免的开销。所以,通常人们会使用另一种文件格式,例如首先给出字符串的数量,然后是字符串,这将允许:
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
构造函数直接打开文件。 - 始终检查此类操作的结果。
std::ifstream
的bool
和!
运算符被覆盖。所以可以做个简单的测试 - 不需要关闭文件。
std::ifstream
的析构函数将为您完成。 - 有一个关于如何读取文件的标准方法。请看下面。
如果你想读取文件直到 EOF(文件结束)或任何其他条件,你可以简单地使用 while 循环并调用提取运算符 >>
例如:
while (REF >> list_name) {
titles.push_back(list_name);
}
为什么这行得通?提取运算符将始终 return 对流的引用以及它所调用的内容。因此,您可以想象在读取字符串后,while 将包含 while (REF)
,因为 REF 是由 (REF >> list_name
编辑的 return。并且,如前所述,流的 bool
运算符被覆盖并且 return 是流的状态。如果有任何错误或 EOF,则 if (REF)
将为假。
现在附加条件:与“-1”的比较可以很容易地添加到 while 语句中。
while ((REF >> list_name) and (list_name != "-1")) {
titles.push_back(list_name);
}
这是一个安全的操作,因为布尔值 short-cut 评估。如果第一个条件已经为假,则不会评估第二个。
对于上面的所有 knwo-how,代码可以重构为:
#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";
}