将整个ASCII文件读入C++ std :: string

Esc*_*alo 559 c++ string file-io caching standard-library

我需要将整个文件读入内存并将其放在C++中std::string.

如果我把它读成a char[],答案很简单:

std::ifstream t;
int length;
t.open("file.txt");      // open input file
t.seekg(0, std::ios::end);    // go to the end
length = t.tellg();           // report location (this is the length)
t.seekg(0, std::ios::beg);    // go back to the beginning
buffer = new char[length];    // allocate memory for a buffer of appropriate dimension
t.read(buffer, length);       // read the whole file into the buffer
t.close();                    // close file handle

// ... Do stuff with buffer here ...
Run Code Online (Sandbox Code Playgroud)

现在,我想做同样的事情,但是使用a std::string而不是a char[].我想避免环路,即我希望:

std::ifstream t;
t.open("file.txt");
std::string buffer;
std::string line;
while(t){
std::getline(t, line);
// ... Append line to buffer and go on
}
t.close()
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

Jer*_*fin 765

有几种可能性.我喜欢使用stringstream作为中间人:

std::ifstream t("file.txt");
std::stringstream buffer;
buffer << t.rdbuf();
Run Code Online (Sandbox Code Playgroud)

现在"file.txt"的内容以字符串形式提供buffer.str().

另一种可能性(虽然我当然也不喜欢它)更像你的原创:

std::ifstream t("file.txt");
t.seekg(0, std::ios::end);
size_t size = t.tellg();
std::string buffer(size, ' ');
t.seekg(0);
t.read(&buffer[0], size); 
Run Code Online (Sandbox Code Playgroud)

正式地说,这不需要在C++ 98或03标准下工作(字符串不需要连续存储数据)但事实上它适用于所有已知的实现,而C++ 11及更高版本确实需要连续的存储,所以它保证与他们合作.

至于为什么我不喜欢后者:首先,因为它更长,更难阅读.其次,因为它需要用你不关心的数据初始化字符串的内容,然后立即写入数据(是的,初始化的时间通常与读数相比是微不足道的,所以它可能无关紧要,但对我来说,它仍然感觉有点不对劲).第三,在文本文件中,文件中的位置X并不一定意味着您将读取X字符以达到该点 - 不需要考虑线端翻译之类的内容.在进行此类翻译的真实系统(例如,Windows)上,翻译后的表单比文件中的更短(即,文件中的"\ r \n"在翻译后的字符串中变为"\n")所以你所做的一切保留了一些你从未使用的额外空间.再说一遍,并不是真的会造成重大问题,但无论如何都会感到有点不对劲.

  • 这应该被标记为答案. (77认同)
  • 确保#include <sstream> (43认同)
  • 对于一些人来说,至少在我的实施方面,三线工作至少与50 fKB以下文件的C fopen替代方案一样重要.过去,它似乎快速失去了性能.在这种情况下,只需使用第二个解决方案. (32认同)
  • 三衬里就像一个魅力! (30认同)
  • 大多数情况下,你没有*测试文件是否已打开(其他操作将失败).作为一项规则,您应该*避免*在现场打印错误消息,除非您确定它适合程序的其余部分 - 如果您必须执行*某些*,通常最好抛出异常.您几乎从不显式关闭文件 - 析构函数会自动执行此操作. (17认同)
  • 不会构造一个空字符串,然后在它上面调用`reserve(size)`会更有效吗? (4认同)
  • 如果有人仍然感兴趣,可以在[ifstream doc](http://www.cplusplus.com/reference/istream/istream/read/)中找到dhardy问题的答案:"此函数只是复制一个块数据,不检查其内容,也不在末尾添加空字符." (3认同)
  • @anthropomorphic你不应该使用reserve(),因为没有正确维护size()信息并且字符串处于损坏状态! (3认同)
  • 经过几分钟的困惑(编译器错误——Windows 10、VS2015),我发现我需要同时包含 `#include &lt;sstream&gt;` 和 `#include &lt;fstream&gt;`。祝你好运! (3认同)
  • 还应检查文件是否已打开,例如,`if(!t)std :: cerr <<"打开文件时出错." << std :: endl;`.当然,完成后不要忘记关闭文件. (2认同)
  • 根据我的测试(GCC 4.7),无论使用哪个行结尾,缓冲区都包含与文件大小相同的字符数。我猜`read(buf, size)` 会关闭这些转换——有人知道吗? (2认同)
  • @Jasen:不是 - 你想在*读取之前设置长度*,这样你就有足够的空间来阅读.当你调用`read`时,设置大小为时已晚. (2认同)
  • @Jasen 可以使用 `std::basic_istream::gcount` 函数获取总读取字符数(不是字节/字符!)。我相信应该通过添加一个 `buffer.resize(t.gcount());` 来去除未使用的字节。 (2认同)
  • 正如 @RaffiKhatchadourian 提到的,缺少错误检查。每当您使用文件时,我强烈建议您进行一些错误处理。 (2认同)

Tyl*_*nry 505

更新:事实证明,这种方法,虽然很好地遵循STL习语,实际上效率低得惊人!不要对大文件这样做.(见:http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html)

你可以从文件中创建一个streambuf迭代器并用它初始化字符串:

#include <string>
#include <fstream>
#include <streambuf>

std::ifstream t("file.txt");
std::string str((std::istreambuf_iterator<char>(t)),
                 std::istreambuf_iterator<char>());
Run Code Online (Sandbox Code Playgroud)

不确定从哪里获取t.open("file.txt", "r")语法.据我所知,这不是一种方法std::ifstream.看起来你已经把它与C混淆了fopen.

编辑:还要注意字符串构造函数的第一个参数周围的额外括号.这些都很重要.它们可以防止被称为" 最令人烦恼的解析 "的问题,在这种情况下,它实际上不会像通常那样给你一个编译错误,但会给你带来有趣的(读取:错误的)结果.

遵循KeithB在评论中的观点,这里有一种方法可以预先分配所有内存(而不是依赖于字符串类的自动重新分配):

#include <string>
#include <fstream>
#include <streambuf>

std::ifstream t("file.txt");
std::string str;

t.seekg(0, std::ios::end);   
str.reserve(t.tellg());
t.seekg(0, std::ios::beg);

str.assign((std::istreambuf_iterator<char>(t)),
            std::istreambuf_iterator<char>());
Run Code Online (Sandbox Code Playgroud)

  • 基准测试:Tyler的解决方案在267 MB文件上大约需要21秒.Jerry的第一次需要1.2秒,他的第二次是0.5(+/- 0.1),所以显然Tyler的代码效率低下. (80认同)
  • 不确定为什么人们会投票,这是一个简单的问题,比如说我有一个1MB的文件,"end"会传递给std :: string构造函数或者调用assign方法多少次?人们认为这些解决方案很优雅,实际上它们是如何做到的优秀例子. (45认同)
  • @KeithB如果效率很重要,你可以找到与`char*`示例相同的文件长度,并调用`std :: string :: reserve`来预先分配必要的空间. (7认同)
  • 疯狂的博客文章是针对一个稍微不同的问题的基准解决方案:它将文件读取为二进制而不是文本,因此没有行结尾的转换.作为副作用,读取为二进制使得ftell成为获取文件长度的可靠方法(假设long可以表示文件长度,这是无法保证的).为了确定长度,文本流上的ftell不可靠.如果您正在从磁带读取文件(例如,备份),额外搜索可能是浪费时间.许多博客文章实施都不使用RAII,因此如果出现错误就会泄漏. (7认同)
  • open肯定是ifstream的一种方法,但是第二个参数是错误的.http://www.cplusplus.com/reference/iostream/ifstream/open/ (4认同)
  • @dhardy你是对的.在我撰写这篇文章大约一年后,有人对这个问题的各种方法进行了一些基准测试,发现不幸的是,保留+分配看起来并不像你希望的那样.事实证明,通常迭代器会产生大量的开销.令人失望.编辑到这个帖子. (4认同)
  • @KeithB:当然,`read()`方法无疑会有很多循环.问题不在于它是否循环而是在何处以及如何明确地循环. (2认同)
  • 使用C ++ 17,您可以相当好地缩短`std :: string`的初始化行(和类似的str.assign`方法一样):`std :: string str {std :: istreambuf_iterator {in},{}} ;`。它使用C ++ 11大括号初始化语法和C ++ 17推导指南(省略了&lt;char&gt;)。 (2认同)

mil*_*ili 64

我认为最好的方法是使用字符串流.简单快捷!!!

#include <fstream>
#include <iostream>
#include <sstream> //std::stringstream
int main() {
    std::ifstream inFile;
    inFile.open("inFileName"); //open the input file

    std::stringstream strStream;
    strStream << inFile.rdbuf(); //read the file
    std::string str = strStream.str(); //str holds the content of the file

    std::cout << str << "\n"; //you can do anything with the string!!!
}
Run Code Online (Sandbox Code Playgroud)

  • @YngveSneenLindal或让析构函数自动执行 - 利用C++! (18认同)
  • 简单快捷,对!http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html (5认同)
  • 记得以后关闭小溪...... (3认同)
  • 既然杰里·科芬(Jerry Coffin)多年前的答案中已经包含了这个内容,为什么还要发布这个? (2认同)

Ank*_*rya 11

你可能在任何书籍或网站上都找不到这个,但我发现它运作得很好:

ifstream ifs ("filename.txt");
string s;
getline (ifs, s, (char) ifs.eof());
Run Code Online (Sandbox Code Playgroud)

  • 将'eof`转换为`(char)`有点狡猾,暗示某种相关性和普遍性是虚幻的.对于`eof()`和signed`char`的一些可能值,它将给出实现定义的结果.直接使用例如`char(0)`/`'\ 0'`将更加健壮并且诚实地指示正在发生的事情. (8认同)
  • 只有在文件中没有"eof"(例如0x00,0xff,...)字符时,这才有效.如果有,您将只阅读文件的一部分. (3认同)
  • @TonyD.关于将eof()转换为char的好处.我想对于老式的ascii字符集,传递任何负值(msb设置为1)都可以.但传递\ 0(或负值)不适用于宽字节或多字节输入文件. (2认同)

mad*_*adx 6

尝试以下两种方法之一:

string get_file_string(){
    std::ifstream ifs("path_to_file");
    return string((std::istreambuf_iterator<char>(ifs)),
                  (std::istreambuf_iterator<char>()));
}

string get_file_string2(){
    ifstream inFile;
    inFile.open("path_to_file");//open the input file

    stringstream strStream;
    strStream << inFile.rdbuf();//read the file
    return strStream.str();//str holds the content of the file
}
Run Code Online (Sandbox Code Playgroud)