最近做一个C语言的嵌入式项目,需要分段向指定内存调用vsnprintf
输出不定长度的格式化输出,因为是分段输出,而且长度不定,所以一开始就不能分配固定长度内存,每次输出都要从输出到上次的结尾开始,所以还要记录每次的输出长度。还是Java开发方便,有现成的StringBuffer
可以用,不停的向StringBuffer
调用 append
添加就好了,哪有这么麻烦。
为了解决这个麻烦,我参照Java中的StringBuffer
对象,实现了一个 stringbuffer
,并基于它实现bufprintf函数可以向stringbuffer
格式化输出,调用时就不需要再考虑自动分配内存和偏移量的问题了。
以下是可以直接运行的完整代码:
stringbuffer_test.c
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
typedef struct
{
char *buffer;
size_t length;
size_t offset;
} stringbuffer;
static char* ensure(stringbuffer * const p, size_t needed)
{
char *newbuffer = NULL;
size_t newsize = 0;
if ((p == NULL) || (p->buffer == NULL))
{
return NULL;
}
if ((p->length > 0) && (p->offset >= p->length))
{
return NULL;
}
if (needed > INT_MAX)
{
return NULL;
}
needed += p->offset + 1;
if (needed <= p->length)
{
return p->buffer + p->offset;
}
if (needed > (INT_MAX / 2))
{
if (needed <= INT_MAX)
{
newsize = INT_MAX;
}
else
{
return NULL;
}
}
else
{
newsize = ((needed + 256 - 1) >> 8 << 8);
}
newbuffer = (char*)realloc(p->buffer, newsize);
if (newbuffer == NULL)
{
free(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
p->length = newsize;
p->buffer = newbuffer;
return newbuffer + p->offset;
}
int bufprintf(stringbuffer* const pbuf, const char*fmt, ...)
{
if (!pbuf->buffer || !pbuf->length)
{
printf("INVALID ARGUMENT:pbuf is EMPTY\n");
return -1;
}
va_list args;
va_start(args, fmt);
size_t bufsz = pbuf->length - pbuf->offset;
int wsz = vsnprintf(pbuf->buffer + pbuf->offset, bufsz, fmt, args);
va_end(args);
if (wsz < 0)
{
if (errno == ERANGE)
{
va_list args1;
va_start(args1, fmt);
wsz = vsnprintf(NULL, 0, fmt, args1);
char *output = ensure(pbuf, wsz);
va_end(args1);
if(!output)
{
printf("MEM ERROR\n");
return -1;
}
bufsz = pbuf->length - pbuf->offset;
va_list args2;
va_start(args2, fmt);
wsz = vsnprintf(output, bufsz, fmt, args2);
va_end(args2);
assert(wsz < bufsz);
}
else {
printf("vsnprintf ERROR %d:%s for fmt:[%s]\n", errno, strerror(errno), fmt);
return -1;
}
}
else if (wsz >= bufsz)
{
char *output = ensure(pbuf, wsz);
if(!output)
{
printf("MEM ERROR\n");
return -1;
}
bufsz = pbuf->length - pbuf->offset;
va_list args;
va_start(args, fmt);
wsz = vsnprintf(output, bufsz, fmt, args);
va_end(args);
assert(wsz < bufsz);
}
pbuf->offset += wsz;
return 0;
}
int stringbuffer_init(stringbuffer *const pbuf, size_t length)
{
static const size_t default_buffer_size = 256;
if (!pbuf)
{
printf("INVALID ARGUMENT:pbuf is NULL\n");
return -1;
}
if (!length) {
length = default_buffer_size;
}
void *p = malloc(length);
if(!p)
{
printf("MEM ERROR\n");
return -1;
}
pbuf->buffer = (char*)p;
pbuf->length = length;
pbuf->offset = 0;
return 0;
}
void stringbuffer_uninit(stringbuffer *const pbuf, bool free_self)
{
if (pbuf && pbuf->buffer)
{
free(pbuf->buffer);
pbuf->buffer = NULL;
pbuf->length = 0;
pbuf->offset = 0;
if (free_self)
{
free(pbuf);
}
}
}
int main()
{
stringbuffer sbuf;
int c = stringbuffer_init(&sbuf, 0);
if (c == 0)
{
bufprintf(&sbuf, "hello %s\n", "jerry");
bufprintf(&sbuf, "welcome to my party\n", "jerry");
printf("sbuf content:\n%s\n", sbuf.buffer);
stringbuffer_uninit(&sbuf, false);
}
else
{
printf("stringbuffer init fail\n");
}
}
如果使用MSVC编译器,如在VS 开发人员提示(CMD)下执行 cl stringbuffer_test.c
就可以直接编译出stringbuffer_test.exe
,运行就能看到效果。
gcc下编译也很简单:
>gcc stringbuffer_test.c
>a.exe
sbuf content:
hello jerry
welcome to my party
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)