如何在不复制的情况下将 std::string 的一部分放入 streambuf 中

How to get part of a std::string into a streambuf without copying?

本文关键字:string 一部分 streambuf 复制 情况下 std      更新时间:2023-10-16

我最近经常使用boost asio,我发现我正在使用std::stringasio::streambuf很多。我发现作为解析网络数据的一部分,我正在尝试在 streambuf 秒和string秒之间来回获取数据。一般来说,我不想弄乱"格式化io",所以iostream不是很有用。我发现,虽然ostream::operator<<(),尽管有官方文档,似乎将我的string转发到streambuf不受干扰,但istream::operator>>()破坏了我streambuf的内容(正如您所期望的那样,因为它是"格式化的")。

在我看来,标准库确实缺少大量用于处理streambuf s和string s以及未格式化io的迭代器和流对象。例如,如果我想将string的子字符串放入streambuf中,如何在不创建string副本的情况下做到这一点?基本的全进全出转移可以像以下方式完成:

// Get a whole string into a streambuf, and then get the whole streambuf back
//  into another string
{
    boost::asio::streambuf sbuf;
    iostream os(&sbuf);
    string message("abcdefghijk lmnopqrs tuvwxyz");
    cout << "message=" << message << endl;
    os << message;
    std::istreambuf_iterator<char> sbit(&sbuf);
    std::istreambuf_iterator<char> end;
    std::string sbuf_it_wholestr(sbit, end);
    cout << "sbuf_it_wholestr=" << sbuf_it_wholestr << endl;    
}

指纹:

message=abcdefghijk lmnopqrs tuvwxyz
sbuf_it_wholestr=abcdefghijk lmnopqrs tuvwxyz

如果我想只将streambuf的一部分放入字符串中,这似乎非常困难,因为istreambuf_iterator不是随机访问迭代器,也不支持算术:

// Get a whole string into a streambuf, and then get part of the streambuf back
//  into another string. We can't do this because istreambuf_iterator isn't a
//  random access iterator!
{
    boost::asio::streambuf sbuf;
    iostream os(&sbuf);
    string message("abcdefghijk lmnopqrs tuvwxyz");
    cout << "message=" << message << endl;
    os << message;
    std::istreambuf_iterator<char> sbit(&sbuf);
    // This doesn't work
    //std::istreambuf_iterator<char> end = sbit + 7; // Not random access!
    //std::string sbuf_it_partstr(sbit, end);
    //cout << "sbuf_it_partstr=" << sbuf_it_partstr << endl;    
}    

而且似乎没有任何方法可以直接使用 string::iteratorstring的一部分转储到streambuf中:

// istreambuf_iterator doesn't work in std::copy either
{
    boost::asio::streambuf sbuf;
    iostream os(&sbuf);
    string message("abcdefghijk lmnopqrs tuvwxyz");
    cout << "message=" << message << endl;
    std::istreambuf_iterator<char> sbit(&sbuf);
    //std::copy(message.begin(), message.begin()+7, sbit); // Doesn't work here
}    

如果我不介意格式化io,我总是可以从streambuf中提取部分string s,但我这样做 - 格式化io几乎从来都不是我想要的:

// Get a whole string into a streambuf, and then pull it out using an ostream
// using formatted output
{
    boost::asio::streambuf sbuf;
    iostream os(&sbuf);
    string message("abcdefghijk lmnopqrs tuvwxyz");
    cout << "message=" << message << endl;
    string part1, part2;
    os << message;
    os >> part1;
    os >> part2;
    cout << "part1=" << part1 << endl;    
    cout << "part2=" << part2 << endl;    
}

指纹:

message=abcdefghijk lmnopqrs tuvwxyz
part1=abcdefghijk
part2=lmnopqrs

如果我对丑陋的副本没问题,我可以生成一个子字符串,当然 - std::string::iterator随机访问......

// Get a partial string into a streambuf, and then pull it out using an
//  istreambuf_iterator
{
    boost::asio::streambuf sbuf;
    iostream os(&sbuf);
    string message("abcdefghijk lmnopqrs tuvwxyz");
    cout << "message=" << message << endl;
    string part_message(message.begin(), message.begin()+7);
    os << part_message;
    cout << "part_message=" << part_message << endl;
    std::istreambuf_iterator<char> sbit(&sbuf);
    std::istreambuf_iterator<char> end;
    std::string sbuf_it_wholestr(sbit, end);
    cout << "sbuf_it_wholestr=" << sbuf_it_wholestr << endl;    
}

指纹:

message=abcdefghijk lmnopqrs tuvwxyz
part_message=abcdefg
sbuf_it_wholestr=abcdefg

stdlib 还有一个奇怪的独立std::getline(),它可以让您从ostream中提取单个行:

// If getting lines at a time was what I wanted, that can be accomplished too...          
{    
    boost::asio::streambuf sbuf;
    iostream os(&sbuf);
    string message("abcdefghijk lmnopqrs tuvwxyzn1234 5678n");
    cout << "message=" << message << endl;
    os << message;
    string line1, line2;
    std::getline(os, line1);
    std::getline(os, line2);
    cout << "line1=" << line1 << endl;
    cout << "line2=" << line2 << endl;
}

指纹: message=abcdefghijk lmnopqrs tuvwxyz 1234 5678

line1=abcdefghijk lmnopqrs tuvwxyz
line2=1234 5678

我觉得我错过了一些罗塞塔石碑,如果我发现它,处理std::stringasio::streambuf会容易得多。是否应该放弃std::streambuf界面并利用asio::mutable_buffer,我可以摆脱asio::streambuf::prepare()

  1. iStream::operator>>() 破坏了我的 Streambufs 的内容(正如您所期望的那样,因为它是"格式化的")。

    使用std::ios::binary标志打开输入流并使用is >> std::noskipws进行操作

  2. 例如,如果我想将字符串的子字符串放入 streambuf,如何在不创建字符串副本的情况下做到这一点?基本的全进全出转移可以像

    尝试喜欢

     outstream.write(s.begin()+start, length);
    

    或使用boost::string_ref

     outstream << boost::string_ref(s).instr(start, length);
    

  3. 而且似乎没有任何方法可以直接使用 string::iterators 将字符串的一部分转储到 streambuf 中:

     std::copy(it1, it2, ostreambuf_iterator<char>(os));
    
  4. 重新解析消息行:

    您可以使用 iter_split 拆分为迭代器范围。

    您可以使用boost::spirit::istream_iterator即时解析嵌入式语法