C 达到极限后恢复[vf]?nprintf
我有一个应用程序,它使用snprintf和vsnprintf将字符串打印到缓冲区。当前,如果它检测到溢出,它会在字符串末尾附加一个>作为字符串被切碎的标志,并向stderr打印一条警告。我试图找到一种方法让它在另一个缓冲区中[从它停止的地方]恢复字符串 如果这是使用strncpy,那么就很容易了;我知道写入了多少字节,因此我可以从*(p+bytes\u-writed)开始下一次打印;然而,对于printf,我有两个问题;首先,格式说明符可能会像在格式字符串中一样在最终字符串中占用更多或更少的空间,其次,我的valist可能会被部分解析 有人有简单的解决办法吗C 达到极限后恢复[vf]?nprintf,c,printf,C,Printf,我有一个应用程序,它使用snprintf和vsnprintf将字符串打印到缓冲区。当前,如果它检测到溢出,它会在字符串末尾附加一个>作为字符串被切碎的标志,并向stderr打印一条警告。我试图找到一种方法让它在另一个缓冲区中[从它停止的地方]恢复字符串 如果这是使用strncpy,那么就很容易了;我知道写入了多少字节,因此我可以从*(p+bytes\u-writed)开始下一次打印;然而,对于printf,我有两个问题;首先,格式说明符可能会像在格式字符串中一样在最终字符串中占用更多或更少的空间
编辑:我应该澄清一下,我正在使用的嵌入式系统内存有限,没有动态分配[即,我不想使用动态分配]。我可以打印255字节的消息,但不能打印更多,尽管我可以打印任意数量的消息。但是,我没有足够的内存在堆栈上分配大量内存,而且我的打印函数需要线程安全,因此我不能只分配一个全局/静态数组 C99函数
snprintf()
和vsnprintf()
都返回打印包含所有参数的整个格式字符串所需的字符数
如果您的实现符合C99,那么您可以为输出字符串创建一个足够大的数组,然后根据需要处理它们
int chars_needed = snprintf(NULL, 0, fmt_string, v1, v2, v3, ...);
char *buf = malloc(chars_needed + 1);
if (buf) {
snprintf(buf, chars_needed + 1, fmt_string, v1, v2, v3, ...);
/* use buf */
free(buf);
} else {
/* no memory */
}
C99函数
snprintf()
和vsnprintf()
都返回打印包含所有参数的整个格式字符串所需的字符数
如果您的实现符合C99,那么您可以为输出字符串创建一个足够大的数组,然后根据需要处理它们
int chars_needed = snprintf(NULL, 0, fmt_string, v1, v2, v3, ...);
char *buf = malloc(chars_needed + 1);
if (buf) {
snprintf(buf, chars_needed + 1, fmt_string, v1, v2, v3, ...);
/* use buf */
free(buf);
} else {
/* no memory */
}
我不认为你能做你想要做的事情(除了通过直接的方式将缓冲区重新分配到必要的大小并再次执行整个操作) <>你列出的原因是一对夫妇的贡献者,但真正的杀手是格式化程序可能在格式化一个参数的时候,当它用完了空间,没有合理的方法重新启动。 例如,假设缓冲区中还有3个字节,格式化程序开始对值
-1234567
进行“%d”转换。它会将“-1\0”放入缓冲区,然后执行任何其他需要执行的操作,以返回您真正需要的缓冲区大小
除了能够确定格式化程序使用的是哪个说明符之外,还需要能够弄清楚,在第二轮中,您需要传递的不是-1234567
,而是234567
。我不相信你能想出一个合理的方法来做那件事
现在,如果您不想从顶部重新启动操作的真正原因是,您可能可以使用分解格式字符串的内容来包装snprintf()
/vsnprintf()
调用,一次只发送一个转换说明符,并将结果连接到输出缓冲区。您必须想出一些方法让包装器在重试过程中保持一些状态,以便它知道从哪个转换规范中提取
因此,从某种意义上说,这可能是可行的,但要避免更简单的“完全重试”方案,似乎需要做大量的工作。我可以看到,也许(也许)在一个系统上尝试这种方法,在这个系统中,您不需要动态分配更大的缓冲区(也许是嵌入式系统)。在这种情况下,我可能会认为需要一个更简单/受限制的范围格式化程序,它没有printf()
格式化程序的所有灵活性,并且可以处理重试(因为它们的范围更有限)
但是,老兄,无论谁说这是一项要求,我都会尽力让他明白一些道理
编辑:
事实上,我收回了一些。如果您愿意使用自定义版本的
snprintf()
(我们称之为snprintf_ex()
),我可以认为这是一个相对简单的操作:
int snprintf_ex( char* s, size_t n, size_t skipChars, const char* fmt, ...);
snprintf_ex()
(及其配套函数,如vsnprintf()
)将把字符串格式化到提供的缓冲区中(与往常一样),但将跳过第一个skipChars
字符的输出
使用编译器库中的源代码(或使用类似的东西)作为起点,您可能可以非常轻松地装配此程序。使用此选项可能类似于:
int bufSize = sizeof(buf);
char* fmt = "some complex format string...";
int needed = snprintf_ex( buf, bufSize, 0, fmt, arg1, arg2, etc, etc2);
if (needed >= bufSize) {
// dang truncation...
// do whatever you want with the truncated bits (send to a logger or whatever)
// format the rest of the string, skipping the bits we already got
needed = snprintf_ex( buf, bufSize, bufSize - 1, fmt, arg1, arg2, etc, etc2);
// now the buffer contains the part that was truncated before. Note that
// you'd still need to deal with the possibility that this is truncated yet
// again - that's an exercise for the reader, and it's probably trickier to
// deal with properly than it might sound...
}
一个缺点(这可能是可以接受的,也可能是不可以接受的)是,格式化程序将从一开始就重新执行所有格式化工作-它只会丢弃它产生的第一个
skipChars
字符。如果我必须使用这样的东西,我认为这几乎肯定是可以接受的(当有人使用标准的snprintf()
函数族处理截断时会发生这种情况)。我认为你不能做你想做的事(除了通过将缓冲区重新分配到所需大小并再次执行整个操作的简单方法之外)
<>你列出的原因是一对夫妇的贡献者,但真正的杀手是格式化程序可能在格式化一个参数的时候,当它用完了空间,没有合理的方法重新启动。
例如,假设缓冲区中还有3个字节,格式化程序开始对值-1234567
进行“%d”转换。它会将“-1\0”放入缓冲区,然后执行任何其他需要执行的操作以返回真正需要的缓冲区大小
除了能够确定格式化程序使用的是哪个说明符之外,还需要能够弄清楚,在第二轮中,您需要通过的不是-1234567
,而是234567