How to calculate the length of output that sprintf will generate?

后端 未结 7 1967
谎友^
谎友^ 2021-02-07 01:35

Goal: serialize data to JSON.

Issue: i cant know beforehand how many chars long the integer is.

i thought a good way to do this

相关标签:
7条回答
  • 2021-02-07 01:44

    You can call int len = snprintf(NULL, 0, "{data:%d}", 12312) to test how much space you need.

    snprintf will print at most size characters, where size is the second argument, and return how many characters would have been necessary to print the whole thing, not counting the terminating '\0'. Because you pass in 0, it won't actually write anything out (and thus will avoid any null pointer exception that would happen by trying to dereference NULL), but it will still return the length that is needed to fit the whole output, which you can use to allocate your buffer.

    At that point you can allocate and print to your buffer, remembering to include one more for the trailing '\0':

    char *buf = malloc(len + 1);
    snprintf(buf, len + 1, "{data:%d}", 12312);
    
    0 讨论(0)
  • 2021-02-07 01:44

    Calling snprintf(nullptr, 0, ...) does return the size but it has performance penalty, because it will call IO_str_overflow and which is slow.

    If you do care about performance, you can pre-allocate a dummy buffer and pass its pointer and size to ::snprintf. it will be several times faster than the nullptr version.

    template<typename ...Args>
    size_t get_len(const char* format, Args ...args) {
      static char dummy[4096]; // you can change the default size
      return ::snprintf(dummy, 4096, format, args...) + 1; // +1 for \0
    }
    
    0 讨论(0)
  • 2021-02-07 01:45

    This isn't strictly an answer to your question, but you may find it helpful nonetheless. It is not portable, but if you're running this on glibc, you can simply use asprintf() instead, which will do the memory allocation for you.

    0 讨论(0)
  • 2021-02-07 01:50

    Since C is where simple language, there is no such thing as "disposable buffers" -- all memory management are on programmers shoulders (there is GNU C compiler extensions for these but they are not standard).

    cant know beforehand how many chars long the integer is.

    There is much easier solution for your problem. snprintf knows!

    On C99-compatible platforms call snprintf with NULL as first argument:

    ssize_t bufsz = snprintf(NULL, 0, "{data:%d}",12312);
    char* buf = malloc(bufsz + 1);
    snprintf(buf, bufsz + 1, "{data:%d}",12312);
    
    ...
    
    free(buf);
    

    In older Visual Studio versions (which have non-C99 compatible CRT), use _scprintf instead of snprintf(NULL, ...) call.

    0 讨论(0)
  • 2021-02-07 01:51

    To just obtain the length you can write:

    int length = snprintf(NULL, 0, "{data:%d}", 12312);
    

    Note that the return type is int. It may return -1 in case of some sort of error. Make sure your input data doesn't include long strings that might cause the total length to exceed INT_MAX !

    0 讨论(0)
  • 2021-02-07 01:59

    If you check the performance, you will running snprintf without an output buffer will take roughly the same time as a full invocation.

    So I recommend you to use a smaller buffer just in case, and only call it a second time if the returned size exceeded the buffer size.

    This uses C++'s std::string but I guess you can adapt it for your needs.

    std::string format(const char* format, ...) {
        va_list args;
        va_start(args, format);
        char smallBuffer[1024];
        int size = vsnprintf(smallBuffer, sizeof smallBuffer, format, args);
        va_end(args);
    
        if (size < sizeof smallBuffer) 
            return std::string(smallBuffer);
    
        char buffer[size  + 1]; /* maybe malloc if it's too big */
    
        va_start(args, format);
        vsnprintf(buffer, sizeof buffer, format, args);
        va_end(args);
        return std::string(buffer);
    }
    

    This code will run 2x faster for strings under 1k compared to the longer ones.

    0 讨论(0)
提交回复
热议问题