views:

522

answers:

5

Hi

I was trying to output a not null terminated char array to a file.

Actual thing is, I am receiving packets and then printing their fields.

Now as these fields are not null terminated, for example, a data segment which has size of 512 but may or may not be completely occupied.

When I write this data to a file I am using simple << overloaded function which does not know any thing about actual data and only looks for termination of data segment.

So, how can I tell the output function to write only this much specific number of bytes?

Instead of using something like this which is expensive to call each time:

enter code here  

bytescopied = strncpy(dest, src, maxbytes);

if (bytescopied < 0) { // indicates no bytes copied, parameter error

    throw(fit);          // error handler stuff here

 } else if (bytescopied == maxbytes) {

    dest[maxbytes-1] = '\0';   // force null terminator

}
+3  A: 

A cheap solution would be to have a buffer that has space for an extra null character and just put a null character at the point when you know the actual size and then output the null-terminated buffer as you already do. Fast and reliable.

sharptooth
Yep, you can save that extra character into a temporary variable. Once the printing is done, just restore it.
yves Baumes
+5  A: 

If you want to put exactly maxbytes bytes, use write method

stream.write(buffer, maxbytes);

If you can have less bytes in buffer, how do you know how many of them your buffer contains? If '\0' marks buffer end, you can write:

stream.write(buffer, std::find(buffer, buffer+maxbytes, '\0') - buffer);
Tadeusz Kopec
In your second call, the entire second parameter is just a roundabout way of calling plain old `strlen`. But in that case, you may as well just use the more idiomatic way for the entire statement: `stream << buffer`.
Rob Kennedy
No. strlen won't stop after maxbytes.
Tadeusz Kopec
`strnlen` will. A strnlen call will look much cleaner: `strnlen(buffer, maxbytes)`. No mucking about with pointers or anything
gnud
`strnlen` has one downside - it's not in standard.
Tadeusz Kopec
+1  A: 

This works, but isn't safe against accidentally calling the standard char* version of operator<<:

#include <iostream>

template <unsigned N>
std::ostream& operator<< ( std::ostream& out, const char ( & data ) [N] )
{
    out.write ( data, N ); 
    // or out.write ( data, strnlen ( data, N ) ); 
    // if you want to stop at a '\0' in the data
    return out;
}


struct Foo {
    char   one[5];
    char   two[1];
    char   three[5];
};

int main ( void )
{
    using namespace std;

    Foo foo = {
        { 'h', 'e', 'l', 'l', 'o' }, 
        { ' ' }, 
        {'w', 'o', 'r', 'l', 'd'} };

    cout << foo.one;
    cout << foo.two;
    cout << foo.three;
    cout << endl;
}

This is safer, using a maxw type which limits the length of the next char* output:

struct maxw {
    unsigned n;
    maxw ( unsigned n ) : n ( n ) { }
};

struct maxw_stream {
    std::ostream& stream;
    unsigned n;
    maxw_stream ( std::ostream& stream, unsigned n ) :
            stream ( stream ),
            n ( n ) {
    }
};

maxw_stream operator<< ( std::ostream& out, const maxw& m )
{
    return maxw_stream ( out, m.n );
}

std::ostream& operator<< ( const maxw_stream& out, const char* data )
{
    out.stream.write ( data, strnlen ( data, out.n ) );
    return out.stream;
}

// eg:
cout << maxw(4) << "Hello World!"  << endl;
// Hell\n
cout << maxw(100) << "Hello World!" << endl;
// Hello World!\n
Pete Kirkham
The problem with strnlen is that AFAIK it's not standard. And your template function is nice, but if a buffer is passed as a plain pointer, not an array, there will be no warning, just standard c-string output function will be called.
Tadeusz Kopec
It's been in C99 for ten years, and IIRC C++0x will bring in all C99 standards.
Pete Kirkham
Well, I searched http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf and couldn't find strnlen.
Tadeusz Kopec
You're quite right - it's POSIX not C99.
Pete Kirkham
A: 

I see mainly two solutions.

In case of ASCII data:

memset(dest,0,destlength); 
bytescopied = strncpy(dest, src, maxbytes);

then You'll always have clear null-terminated string in buffor.

Second in case of ASCII data:

std::string yourASCII(src,maxbytes);
yourASCII.c_str() // would be null terminated.
bua
A: 

If you don't care about the last byte, you can just

buffer[buffersize-1] = 0;

and then feed buffer to whatever string function you want. If it is shorter, everything will run to the null terminator that already exists, and if there was no terminator it will run to the one you just created.

And it's fast :)

Combuster