Search code examples
c++streamstringstreamistringstreamnull-terminated

C++ std::istringstream how to not terminate at '\0'


There is a function Foo that takes in an input stream of char. I want to call Foo from my function Bar, which takes in a C-style array with size. char is used to represent a byte (not using std::byte because it's difficult to get it working with streams).

void Foo(std::basic_istream<char>& stream) { /* ... */ }

// absl::Span<const char> in my code, using char pointer
// and size here for simplicity.
void Bar(const char * const arr, const size_t size) {
  std::basic_istream<char> stream = ArrToStream(arr, size);
  foo(stream);
}

I'm struggling to implement ArrToStream.

If I pass an instance of std::ifstream to Foo, everything works as expected. When it comes to ArrToStream, seems like the most recommended approach is just to use std::istringstream like the following:

std::basic_istream<char> ArrToStream(
  const char * const arr,
  const size_t size
) {
  return std::istringstream(arr, size);
}

However, this only works if there is no '\0' character in the array. There are many 0 bytes in my data, so the input string stream treats them as null terminators and stops reading the stream. I tried calling stream.clear() and stream.seekg(...) but no luck so far, the string stream simply refuse to read past 0's.

Is there a way to read or ignore '\0'? If not, what's the best way to represent byte streams in C++20?


Solution

  • It's not clear (at least to me) what problem you're encountering. It's pretty easy to initialize a stringstream with a string that contains NUL characters:

    #include <sstream>
    #include <cassert>
    #include <string>
    #include <iostream>
    
    int main() { 
        // a string of 100 NUL characters
        std::string data(100, '\0');
    
        // initialize the stream from that string
        std::istringstream input (data);
    
        // initialize a string with other characters:
        std::string data_read(20, '1');
    
        // read data from stream:
        if (input.read(data_read.data(), 20))
            for (int i=0; i<20; i++) {
                // and assure that we read the NULs that we expected:
                assert(data_read[i] == '\0');
            }
        else
            std::cerr << "Read failed\n";
    }
    

    At least when I run this, it succeeds, indicating that the NULs were place in the stream, and that we successfully read them back out afterwards.