How to get characters from stringstream without copy?

What is the correct C ++ 11 way to extract a character set from a string stream without using boost?

I want to do this without copying, if possible, because where it is used is in the critical data loop. It seems that std :: string does not allow direct access to data.

For example, the code below executes a copy of a substring from a string stream:

inline std::string left(std::stringstream ss, uint32_t count) { char* buffer = new char[count]; ss.get(buffer, count); std::string str(buffer); // Second copy performed here delete buffer; return str; } 
  • Should I use char * buffer according to C ++ 11?
  • How do I make a second copy?

I understand that vectors initialize every character, so I want to avoid this.

In addition, this needs to be passed to a function that takes const char *, so now after this run I have to do .c_str (). Does it also make a copy?

It would be nice to be able to pass const char *, but that seems to contradict the "correct" C ++ 11 style.

To understand what I'm trying to do, here is the "efficient" that I want to use for it:

 fprintf( stderr, "Data: [%s]...", left(ststream, 255) ); 

But C ++ 11 forces:

 fprintf( stderr, "Data: [%s]...", left(str_data, 255).c_str() ); 

How many instances of this line can I make here?

How can I reduce it to just one copy from a string?

+6
source share
4 answers

You can use something like described in this link: How to create std :: string directly from char * array without copying?

Basically, create a string, call the resize () method on the string with the size that is passed to your function, and then pass the pointer to the first character of the string to stringstring.get (). You will receive only one copy.

 inline std::string left(std::stringstream& ss, uint32_t count) { std::string str; str.resize(count); ss.get(&str[0], count); return str; } 
+7
source

My suggestion:

  • Create std::string to return by specifying its size.

  • Read the characters one by one from stringstream and set the values ​​to std::string .

This is what the function looks like:

 inline std::string left(std::stringstream ss, uint32_t count) { std::string str(count+1, '\0'); for (uint32_t i = 0; i < count; ++i ) { int c = ss.getc(); if ( c != EOF ) { str[i] = c; } else { break; } } return str; } 
+2
source

R Sahu, I like it! Now it’s clear that I see it. ;-)

I have one mod, though (and also passed shared_ptr to the stream, which was on my version):

In your initializer, you fill with zeros. You just need to fill in the last, so I suggest the following:

 inline std::string left(std::shared_ptr<std::stringstream> ss, uint32_t count) { std::string str; str.reserve(count + 1); uint32_t i; for(i = 0; i < count; ++i) { int c = ss->get(); if(c != EOF) { str[i] = c; } else { break; } } str[i] = '\0'; return str; } 

Now only initialized with zeros for one character.

Thanks R Sahu!

+2
source

If the purpose of this function is intended solely for switching to fprintf or another C-style stream, then you can completely abandon the selection by following these steps:

 void left(FILE *out, std::stringstream &in, size_t count) { in.seekg(0); char ch; while ( count-- && in.get(ch) ) fputc(out, static_cast<unsigned char>(ch)); } 

Using:

 fprintf( stderr, "Data: [" ); left(stderr, stream, 255); fprintf( stderr, "] ...\n"); 

Keep in mind that another seekg if you try later to use stream read functions in a line stream; and this will not surprise me if it is the same speed or slower than the options involving str() .

0
source

Source: https://habr.com/ru/post/982861/