Как получить часть std::string в streambuf без копирования?

В последнее время я часто использую Boost Asio и обнаруживаю, что работаю с std::stringс и asio::streambufдовольно много. Я нахожу, что я пытаюсь получить данные назад и вперед между streambufс и stringЭто как разбор сетевых данных. В общем, я не хочу возиться с "отформатированным IO", поэтому iostreamне очень полезны Я нашел это в то время как ostream::operator<<(), несмотря на официальную документацию, похоже, передает stringс в streambufбезмятежный, istream::operator>>() калечат содержимое моего streambufs (как и следовало ожидать, учитывая, что он "отформатирован").

Мне действительно кажется, что в стандартной библиотеке отсутствует множество итераторов и потоковых объектов для работы с streambufс и stringи неформатный ио. Например, если я хочу получить подстроку 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::iterators, чтобы сбросить часть string в 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
}    

Я всегда могу вытащить частичное stringиз вне streambuf если я не против форматированного io, но я делаю - форматированный 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 tuvwxyz\n1234 5678\n");
    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::string а также asio::streambuf было бы намного проще, если бы я обнаружил это. Следует просто отказаться от std::streambuf интерфейс и использовать asio::mutable_bufferиз которого я могу выбраться asio::streambuf::prepare()?

2 ответа

Решение
  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

Если я ничего не упустил, вам просто нужноstd::streambuf::sputn()

      my_buf.sputn(mystr.data(), mystr.size());
Другие вопросы по тегам