c - 逐部分获取 vsnprintf() 输出
问题描述
我有一个大小为 N 的 char 数组,我需要在不同部分获取 vsnprintf 输出,以防它的长度超过 char 数组大小减 1(N-1 个字节)。
我想实现类似 printf 的东西,但通过 UART 输出它。我不希望 N 大于 100。如果有人想打印超过 100 个字符的字符串,我想分部分打印。我使用过 vsnprintf 但我不知道如何逐部分获取它的输出。也许它不是来自 stdio 库的正确函数,我也查看了 vsnprintf_s 和 _vscprintf 但仍然不知道如何实现我想要做的事情。我不想调用 malloc 也不想使用 VLA,因为我希望最大缓冲区长度为 100,但同时能够逐部分输出超过 100 个字节的字符串。
char char_array[100];
void uart_print(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vsnprintf(char_array, sizeof(char_array), fmt, args); /* Don't get the result because it is not useful for me */
uart_output(char_array);
}
实际结果是将通过 UART 输出的字符串切割成 100 字节。我想要完整的字符串输出。
我想做这样的事情:
char char_array[100];
void uart_print(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int ret;
unsigned int start_index = 0;
size_t max_s = sizeof(char_array);
do {
ret = vsnprintf(char_array, max_s, start_index, fmt, args); /* The new parameter is number 3, it would specify from which point from the generated string it starts to save data in char_array */
uart_output(char_array);
start_index += max_s;
} while (max_s <= ret);
}
解决方案
您可以将格式字符串(和格式)切成更小的段,每个段包含 1 个(或更少)%
转换。
下面是一个例子:
- 仍然不完美
- 也不完整
- 通常,您会将
parse_fmt()
函数与 中的 for 循环格式合并main()
,从而丢失“块”结构数组。 - 即使缓冲区是 100 个字符,也有
fmtbuff[]
; 并且xxxprintf()
函数也需要 X*this 大小。
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
//dummie for testing
void uart_output(char *buff) { while(*buff) putc(*buff++,stdout); }
struct chunk {
unsigned start;
unsigned end; //next%
char ll;
char type;
union {
char c;
int i;
long int li;
long long int lli;
unsigned u;
unsigned long lu;
unsigned long long llu;
long double ld;
double d;
char *s;
void *p;
} u;
} chunks[10];
/* Chop the format string to pieces, with at most one '%' conversion per chunk.
** This is still not perfect,
** not all conversion types art present yet
** and backslash-escapes are not recognised.
*/
#define TINY_BUFFER_SIZE 100
int parse_fmt(struct chunk *arr, const char *fmt, ...) {
unsigned idx,pos;
int ch,state,ll;
va_list args;
va_start(args, fmt);
arr[0].start=0;
arr[0].type=0;
state=0;
ll=0;
for(idx=pos=0; ch=fmt[pos]; pos++) {
// fprintf(stderr,"Idx=%d Pos=%u State=%d Char='%c'\n",idx,pos,state, ch);
switch(state){
case 0:
if(pos-arr[idx].start>TINY_BUFFER_SIZE/2)goto next;
if(ch =='%') {
if (!idx) goto next;
else {state++; continue; }
}
continue;
case 1:
if(ch =='%'){state=0;continue;}
if(pos-arr[idx].start>80)goto next;
state++;
// falltru
case 2:
switch(ch) {
default: // ignore all modifiers except'l'
case'h': continue;
case'z': ll=2;continue;
case'l': ll++;continue;
case 'c': arr[idx].type= 'c';
arr[idx].u.c = va_arg(args,int);
break;
case 'd': arr[idx].type= 'i';
if(ll ==2) arr[idx].u.lli = va_arg(args,long long int);
else if(ll ==1) arr[idx].u.li = va_arg(args,long int);
else arr[idx].u.i = va_arg(args,int);
break;
case 'X':
case 'x':
case 'u': arr[idx].type= 'u';
if(ll ==2) arr[idx].u.llu = va_arg(args,long long unsigned int);
else if(ll ==1) arr[idx].u.lu = va_arg(args,long unsigned int);
else arr[idx].u.u = va_arg(args,unsigned int);
break;
case 'g':
case 'f': arr[idx].type= 'f';
if(ll) arr[idx].u.ld = va_arg(args,long double);
else arr[idx].u.d = va_arg(args,double);
break;
case 's': arr[idx].type= 's';
arr[idx].u.s = va_arg(args,char*);
break;
case 'p': arr[idx].type= 'p';
arr[idx].u.p = va_arg(args,void*);
break;
}
state++; continue;
case 3:
// falltru
next:
arr[idx].ll=ll;
arr[idx].end=pos;
if(idx++) state=0; else state=1;
arr[idx].start=pos;
arr[idx].type=0;
ll=0;
break;
}
}
if(state) goto next; // Haha!
if(idx) arr[idx-1].end=pos;
va_end(args);
return idx;
}
int main(void){
int idx,len,res;
char buff[TINY_BUFFER_SIZE];
char fmtcopy[TINY_BUFFER_SIZE];
char*fmt;
fmt = "Haha! Int=%03d Unsigned=%8u Hex=%08x Char=%c"
"... very long string here... very long string here... very long string here... very long string here... very long string here..."
" Float=%8.7f Double=%g %s OMG the end.\n";
len = parse_fmt(chunks, fmt
, 666, (unsigned)42, (unsigned)0xaa55, '?' , 3.1415926535, sqrt(314.0),"done!"
);
for(idx=0;idx<len;idx++) {
if(0) fprintf(stderr,"%d:{%u,%u}%d,%c"
,idx
, chunks[idx].start
, chunks[idx].end
, chunks[idx].ll
, chunks[idx].type
);
// select the part of the formatstring we are foing to handle
res= chunks[idx].end-chunks[idx].start;
memcpy(fmtcopy,fmt+chunks[idx].start, res);
fmtcopy[res] = 0;
if(0)fprintf(stderr,"\tfmtcopy='%s'", fmtcopy);
switch( chunks[idx].type){
case 0: // no percent sign present
res= snprintf(buff, sizeof buff, "%s", fmtcopy);
break;
case'p':
res =snprintf(buff , sizeof buff, fmtcopy, chunks[idx].u.p);
break;
case's':
res =snprintf(buff , sizeof buff, fmtcopy, chunks[idx].u.s);
break;
case'f':
if(chunks[idx].ll>0) res =snprintf(buff, sizeof buff, fmtcopy, chunks[idx].u.ld);
else res =snprintf(buff , sizeof buff, fmtcopy, chunks[idx].u.d);
break;
case'c':
res =snprintf(buff , sizeof buff, fmtcopy, chunks[idx].u.i);
break;
case'i':
if(chunks[idx].ll>1) res =snprintf(buff, sizeof buff, fmtcopy, chunks[idx].u.lli);
else if(chunks[idx].ll>0) res =snprintf(buff, sizeof buff, fmtcopy, chunks[idx].u.li);
else res =snprintf(buff , sizeof buff, fmtcopy, chunks[idx].u.i);
break;
case'x':
case'u':
if(chunks[idx].ll>1) res =snprintf(buff, sizeof buff, fmtcopy, chunks[idx].u.llu);
else if(chunks[idx].ll>0) res =snprintf(buff, sizeof buff, fmtcopy, chunks[idx].u.lu);
else res =snprintf(buff , sizeof buff, fmtcopy, chunks[idx].u.u);
break;
}
if(0) fprintf(stderr,"\tRes =%d Buff='%s'\n",res ,buff);
uart_output(buff);
}
return 0;
}
推荐阅读
- ruby - 如何在 Ruby on Rails 中使用 Selenium 单击链接?
- spring - 添加 JpaTransactionManager 配置时无法插入数据
- php - 如何运行这个 PHP 网站并将其托管在 HostGator 上?
- module - 在多个文件中使用模块时无法编译项目:“导入只能引用通过 --extern 传递的外部板条箱名称”
- ethereum - 安装 go-ethereum 时如何解决“意外的目录布局”错误?
- jenkins - Jenkins:仅在迁移文件夹已更改时运行迁移
- angularjs - Res.download() 传递数据而不是下载文件
- python - Python multiprocessing - 独立处理字典中的每个键值对
- typescript - 比较角度5的时间
- kendo-ui - Kendo Grid - 将数据源从一个网格复制到另一个网格