Reading directly from an std::istream into an std:

2020-01-29 06:22发布

Is there anyway to read a known number of bytes, directly into an std::string, without creating a temporary buffer to do so?

eg currently I can do it by

boost::uint16_t len;
is.read((char*)&len, 2);
char *tmpStr = new char[len];
is.read(tmpStr, len);
std::string str(tmpStr, len);
delete[] tmpStr;

6条回答
干净又极端
2楼-- · 2020-01-29 06:48

You could use something like getline:

#include <iostream>
#include <string>
using namespace std;

int main () {
  string str;
  getline (cin,str,' ');
}
查看更多
Anthone
3楼-- · 2020-01-29 06:49

An easy way would be:

std::istream& data
const size_t dataSize(static_cast<size_t>(data.rdbuf()->in_avail()));
std::string content;
content.reserve( dataSize);
data.read(&content[0], dataSize);
查看更多
做个烂人
4楼-- · 2020-01-29 06:54

Are you just optimizing code length or trying to save yourself a copy here? What's wrong with the temporary buffer?

I'd argue that you're actually circumventing the protections of the string trying to write directly do it like that. If you're worried about performance of the copy to a std::string because you've identified that it's in some way affecting the performance of your application, I'd work directly with the char*.

EDIT: Doing more looking... initializing std::string from char* without copy

In the second answer, it's stated pretty flatly that you can't achieve what you're looking to achieve (ie. populate a std::string without an iteration over the char* to copy.)

Take a look at your load routine (post it here perhaps?) and minimize allocations: new and delete certainly aren't free so you can at least save some time if you don't have to re-create the buffer constantly. I always find it helpful erase it by memset'ing the buffer to 0 or null terminating the first index of the array each iteration but you may quickly eliminate that code in the interests of performance once you're confident in your algorithm.

查看更多
▲ chillily
5楼-- · 2020-01-29 06:55

std::string has a resize function you could use, or a constructor that'll do the same:

boost::uint16_t len;
is.read((char*)&len, 2);

std::string str(len, '\0');
is.read(&str[0], len);

This is untested, and I don't know if strings are mandated to have contiguous storage.

查看更多
甜甜的少女心
6楼-- · 2020-01-29 07:05

I would use a vector as the buffer.

boost::uint16_t len;
is.read((char*)&len, 2); // Note if this file was saved from a different architecture 
                         // then endianness of these two bytes may be reversed.

std::vector buffer(len);  // uninitialized.
is.read(&buffer[0], len);

std::string  str(buffer.begin(),buffer.end());

Though you will probably get away with using a string as the buffer (as described by GMan). It is not guaranteed by the standard that a strings members are in consecutive locations (so check your current implementation and put a big comment that it needs checking when porting to another compiler/platform).

查看更多
\"骚年 ilove
7楼-- · 2020-01-29 07:08

You could use a combination of copy_n and an insert_iterator

void test_1816319()
{
    static char const* fname = "test_1816319.bin";
    std::ofstream ofs(fname, std::ios::binary);
    ofs.write("\x2\x0", 2);
    ofs.write("ab", 2);
    ofs.close();

    std::ifstream ifs(fname, std::ios::binary);
    std::string s;
    size_t n = 0;
    ifs.read((char*)&n, 2);
    std::istream_iterator<char> isi(ifs), isiend;
    std::copy_n(isi, n, std::insert_iterator<std::string>(s, s.begin()));
    ifs.close();
    _unlink(fname);

    std::cout << s << std::endl;
}

no copying, no hacks, no possibility of overrun, no undefined behaviour.

查看更多
登录 后发表回答