开发者

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 NULLs do not delimit strings:

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++.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜