C++非空终止字符数组输出

7
我试图将一个未以null结尾的char数组输出到文件中。
实际情况是,我正在接收数据包并打印它们的字段。
现在,由于这些字段没有以null结尾,例如,数据段的大小为512,但可能完全没有被占用。
当我将这些数据写入文件时,我使用简单的<<重载函数,它不知道实际数据的任何内容,只寻找数据段的终止。
那么,我该如何告诉输出函数仅写入这么多特定字节数
而不是每次调用都使用昂贵的方法,像这样:
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

}
5个回答

17
如果你想确切地写入 maxbytes 字节,请使用 write 方法。
stream.write(buffer, maxbytes);

如果你的缓冲区中有更少的字节,你如何知道缓冲区中包含了多少个字节?如果'\0'表示缓冲区的结尾,那么你可以这样写:

stream.write(buffer, std::find(buffer, buffer+maxbytes, '\0') - buffer);

1
在你的第二个调用中,整个第二个参数只是一种绕弯路来调用普通的 strlen。但在这种情况下,你可能会更倾向于使用更惯用的方式来完成整个语句:stream << buffer - Rob Kennedy
不,strlen不会在maxbytes之后停止。 - Tadeusz Kopec for Ukraine
strnlen将会变得更加简洁:strnlen(buffer, maxbytes)。不需要玩弄指针或任何东西。 - gnud
2
strnlen有一个缺点 - 它不是标准的。 - Tadeusz Kopec for Ukraine

3

这个方法可以工作,但不安全,因为有可能会意外调用标准的char*版本的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;
}

这样做更加安全,使用限制下一个 char* 输出长度的 maxw 类型:
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

strnlen的问题在于据我所知它不是标准的。而且你的模板函数很好,但如果一个缓冲区被传递为普通指针而不是数组,就不会有警告,只会调用标准的C字符串输出函数。 - Tadeusz Kopec for Ukraine
它在 C99 中已经存在了十年,而我记得 C++0x 将会带来所有的 C99 标准。 - Pete Kirkham
我在 http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf 上搜索,但找不到 strnlen。 - Tadeusz Kopec for Ukraine

3

一种便宜的解决方案是使用一个缓冲区,该缓冲区有额外的空间用于放置一个空字符,在知道实际大小的时候将空字符放置在该位置,然后像您已经做的那样输出以空字符结尾的缓冲区。快速可靠。


是的,您可以将那个额外的字符保存到一个临时变量中。一旦打印完成,只需恢复即可。 - yves Baumes

0

我主要看到两种解决方案。

对于ASCII数据的情况:

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

那么您将始终在缓冲区中拥有清晰的以 null 结尾的字符串。

其次,在 ASCII 数据的情况下:

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

0
如果您不关心最后一个字节,您可以直接进行操作。
buffer[buffersize-1] = 0;

然后将缓冲区提供给您想要的任何字符串函数。如果它更短,一切都将运行到已经存在的空终止符,如果没有终止符,它将运行到您刚刚创建的终止符。

而且速度很快 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接