printf a buffer with nulls
Is there some fancy printf syntax to ignore nulls when printing a string?
Example use case: printing a network packet that contains a bunch of null terminated strings.
My test code to illustrate the issue:
#include <cstdio>
#include <cstring>
void main()
{
char buffer[32];
const char h[] = "hello";
const char w[] = "world";
con开发者_如何学运维st int size = sizeof(w) + sizeof(h);
memcpy(buffer, h, sizeof(h));
memcpy(buffer + sizeof(h), w, sizeof(w));
//try printing stuff with printf
printf("prints only 'hello' [%s]\n",buffer);
printf("this prints '<bunch of spaces> hello' [%*s]\n",size,buffer);
printf("and this prints 'hello' [%.*s]\n",size,buffer);
//hack fixup code
for(int i = 0; i < size; ++i)
{
if(buffer[i] == 0)
buffer[i] = ' ';
}
printf("this prints 'hello world ' [%.*s]\n",size,buffer);
}
printf
assumes strings are c-style (i.e. ending in null characters). You need to do one of the following:
1) use the write
method of ostream (C++ style)
2) use the write
command (from unistd.h
, C style)
3) iterate through each character and print.
When I am debugging packets, oftentimes I will print out each character using the %02hhx
format to be sure I see the exact code (without any quirks about printing null characters to the screen)
There isn't a way to tell printf
the length of the string you want to print. You'll need to print each character individually:
void printbuf(const char* buffer, int len) {
for (int i = 0; i < len; ++i)
printf("%c", buffer[i]);
}
char buf[] = {'a', 'b', 'c', 0, 'a', 'b', 'c'};
printbuf(buf, 7);
// prints
// abc abc
You could wrap the buffer in a string
and print that (if you don't mind the duplication), since NULL
s do not delimit string
s:
char buf[] = {'a', 'b', 'c', 0, 'a', 'b', 'c'};
string strwithnulls(buf, buf + 7); // or as John pointed out, string strwithnulls(buf, 7);
cout << strwithnulls;
// prints
// abc abc
But better yet
you could use write
of std::ostream
, because you are using C++ after all:
char buf[] = {'a', 'b', 'c', 0, 'a', 'b', 'c'};
cout.write(buf, 7); // best yet
If you are actually using C (why'd you tag it C++?) you can do this:
char buf[] = {'a', 'b', 'c', 0, 'a', 'b', 'c'};
fwrite(buf, 1, 7, stdout);
There is nothing that comes to mind, so why not use fwrite() instead. This works assuming you know the format of the contents of your buffer.
fwrite(buffer, size, 1, stdout);
If you require further formatted output you can use printf() before and after the call to fwrite().
I am not responding with references to typical C++ iostreams because I got the impression you preferred something in standard C.
First off, your memcpy
operations copy six bytes each, since both "hello"
and "world"
are char[6]
, and the result is h|e|l|l|o|\0|w|o|r|l|d|\0
. That's why you don't copy strings with memcpy
, but rather with the cleverly named strcpy
.
Next, "how would you print network packets"? I have a utility function hexdump
that prints arbitrary memory in lines of sixteen, maybe you'll find it useful:
void asciiprint(FILE * out, unsigned char c)
{
if (c < 32 || c > 126) fprintf(out, ".");
else fprintf(out, "%c", c);
}
void hexdump(FILE * out, const unsigned char * buf, size_t length, const char * delim)
{
for (size_t k = 0; 16*k < length; k++)
{
fprintf(out, "%s", delim);
for (int i = 0; i < 16 && 16*k + i < length; ++i)
fprintf(out, "0x%02X ", buf[16*k + i]);
if (16*(k+1) > length)
for (size_t i = 0; i < 16*(k+1)-length; ++i)
fprintf(out, " ");
fprintf(out, " ");
for (size_t i = 0; i < 16 && 16*k + i < length; ++i)
asciiprint(out, buf[16*k + i]);
fprintf(out, "\n");
}
}
Usage: hexdump(stdout, (const unsigned char*)buffer, 32, "==> ");
Do you want to ignore nulls, or print them in some visible form?
Assuming the latter, and borrowing @Seth's code:
void printbuf(const char* buffer, int len) {
for (int i = 0; i < len; ++i)
if (buffer[i] == '\0') {
cout << "\\0";
}
else
cout << buffer[i];
}
}
}
Replace cout << ...
if you're using C rather than C++.
精彩评论