【问题标题】:how can I read exactly 128 bytes from an fstream into a string object? [duplicate]如何将 fstream 中的 128 个字节准确地读取到字符串对象中? [复制]
【发布时间】:2026-02-08 17:25:01
【问题描述】:

如何将 fstream 中的 128 个字节准确地读取到字符串对象中?

我编写了一些代码来读取文件的前 128 个字节并打印它,然后再读取文件的最后 128 个字节并打印它。最后一部分有效,因为您可以轻松地迭代到 EOF,但是我如何从前面获得 128 个字节呢?下面的代码不起作用,因为您无法将 128 添加到 ifstream 迭代器,它不可索引,只能递增(似乎)。

当然我可以创建一个迭代器并 *++ 128 次,但必须有一个简单的单行方法来做到这一点,对吧?

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

int main(int argc, char **argv)
{
    std::ifstream ifs ("input.txt",std::ifstream::in | std::ifstream::binary);

    if (ifs.good())
    {
    // read first 128 bytes into a string
        ifs.seekg(0,std::ifstream::beg);
        std::string first128((std::istreambuf_iterator<char>(ifs)),
                             (std::istreambuf_iterator<char>(ifs))+128);

        std::cout << first128 << std::endl;

    // read last 128 bytes into a string
        ifs.seekg(-128,std::ifstream::end);
        std::string last128((std::istreambuf_iterator<char>(ifs)),
                            std::istreambuf_iterator<char>());

        std::cout << last128 << std::endl;

        return 0;
    }

    return 1;
}

【问题讨论】:

  • read/readsome 仅对 char *s 进行操作,您无法从字符串类型中获取 char *(只有 const char *),因此您无法直接使用它们读取字符串类型。

标签: c++ string iostream fstream ifstream


【解决方案1】:
char buffer[129];
ifs.read (buffer,128);
buffer[128] = '\0';
first128 = buffer;

那么这个怎么样:

template <typename Itr, typename Out>
void copy_n(Itr it, size_t count, Out out)
{
    for(size_t i=0;i<count;++i)
      out = *it++;
} 

...

std::string first128; 
std::istreambuf_iterator<char> it(ifs);
copy_n( it, 128,
  std::back_inserter<std::string>(first128) );

【讨论】:

  • 字节浪费!字符缓冲区[128]; ifs.read(缓冲区,128); std::string first128(buffer,128);但我真的想尽可能纯粹地做到这一点。没有尖括号的答案不必适用!
  • @Southern Hospitality:我已经编辑包含另一个版本。
  • 我认为第一个答案是完美的。您想进入一个字符串,只需创建一个大小为 128 的 std::string 并将其作为要读取的缓冲区传递。认为不纯就是说明标准库在某种程度上是不纯的。
  • FWIW,C++0x 在标准库中有一个copy_n,所以你很快就可以消除第二个版本的那部分。
  • dash-tom-bang,您不能在需要 char * 的地方传递 std::string,只能在需要 const char * 的地方传递。感谢 ngoozeff 的第二次努力。
【解决方案2】:

我的回答使用了一个中间缓冲区,但也许你会很高兴它使用迭代器从缓冲区初始化字符串。

std::vector<char> buffer(128); // create a buffer
ifs.read( &buffer[0], buffer.size() ); // read to buffer
std::string first128( buffer.begin(), buffer.end() ); // copy from vector

在我看来,他们对 iostreams 的实现有点太可爱了。尝试将迭代器用于流 I/O 过于复杂。

顺便说一句,我怀疑您正在尝试的实现将在幕后进行各种中间缓冲(可能一些在内核中,一些在库中)以及重新分配和复制字符串几个随着它的增长而倍增。

另一个想法:你真的需要标准字符串中的结果吗?您可能只是从向量开始工作——避免复制到字符串的最后一步。或者,如果您喜欢冒险,您可以创建自己的字符串类,确实允许您以与向量相同的方式公开内部缓冲区。

【讨论】:

  • std::string 是否提供对 C++0x 中连续缓冲区的写访问权限?
  • 缓冲区的字符向量是非常糟糕的主意,非常无效。
  • @Alecs:你能详细说明一下吗?你的意思是它不起作用,还是只是它不是最有效的方法?
  • 它会起作用,但它不仅不是最有效的方法,而且是效率最低的方法之一。纯字符非常有效,如果您害怕使用它们。最好选择 std::string 或其他一些字符串类实现。但是 char 的向量有些变态
  • @Alecs:我从来不知道 vector 是个变态。你对此有什么参考吗?我认为 vector 和 string 可能具有相同的存储空间(尤其是对于 2 的幂分配)。此外,我只是主张分配一个向量——它有多糟糕?我经常用vector来做这种事情,所以我想知道你在说什么。
【解决方案3】:

这里我对streambuffer进行了一些研究,通过构造函数从istream中直接读取字符串:

class mystringbuf : public std::stringbuf
{
public:
    explicit mystringbuf(std::istream& istr, size_t n,
                        std::ios_base::openmode __mode = std::ios_base::in )
    {
        _M_string.resize(n);
        std::stringbuf::_M_stringbuf_init(__mode);
        istr.read(gptr(), n);
    }
public:
    std::stringbuf::char_type* gptr() const
    {
        return std::stringbuf::gptr();
    }
    std::string& str_ref(){
        return _M_string;
    }
};
std::ostream& operator << (std::ostream& ostr, mystringbuf& buf){
    ostr << buf.str_ref();
    return ostr;
}

使用示例:

using std::cout;
using std::endl;

int main()
{
    std::stringbuf buffer;          // empty buffer
    buffer.str("abc def ABC DEF "); // not empty now
    std::istream is (&buffer);      // associate stream buffer to stream
    mystringbuf data(is, 10);       // read 10 bytes
    cout << "data=" <<  data << endl;
    return 0;
}

输出:

data=abc def AB

如果我在某个地方有问题,请转发给我。

【讨论】:

  • istr.read(gptr(), n); 行是 UB。无法保证 n 个字节不会导致溢出。