【问题标题】:Printf Variable String Length SpecifierPrintf 可变字符串长度说明符
【发布时间】:2013-10-09 08:52:38
【问题描述】:

我有一个包含字符串和长度的结构:

typedef struct string {
  char* data;
  size_t len;
} string_t;

这一切都很好,花花公子。但是,我希望能够使用类似printf 的函数输出这个结构的内容。 data 可能没有 nul 终止符(或者放在错误的地方),所以我不能只使用 %s。但是%.*s 说明符需要int,而我有size_t

那么现在的问题是,如何使用printf 输出字符串?

【问题讨论】:

  • 您可以将size_t转换int,前提是值适合...
  • @KerrekSB 好吧,如果长度不适合int,那将是一个有趣的printf 调用:-)) 应该检查一下,因为它可能会溢出。
  • 如果data 可能包含不可打印的字符(如空字符),则您根本不需要%s。写一个循环。

标签: c string


【解决方案1】:

如何使用 printf 输出字符串?

在一次通话中?你不能以任何有意义的方式,因为你说你可能在奇怪的地方有空终止符。一般来说,如果您的缓冲区可能包含不可打印的字符,您需要弄清楚在输出字符串时如何打印(或不打印)这些字符。编写一个循环,测试每个字符,然后按照逻辑要求打印(或不打印)。

【讨论】:

    【解决方案2】:

    假设您的字符串中没有任何嵌入的 NUL 字符,您可以在将 size_t 转换为 int 后使用 %.*s 说明符:

    string_t *s = ...;
    printf("The string is: %.*s\n", (int)s->len, s->data);
    

    这也是假设您的字符串长度小于INT_MAX。如果你的字符串长于INT_MAX,那么你还有其他问题(打印出 20 亿个字符需要相当长的时间,一方面)。

    【讨论】:

    • 不太可能,但 OP可能针对具有 16 位 int 的嵌入式或历史设备
    • 这真的是最好的方法吗?当我第一次注意到这种行为时,我的第一个问题是“为什么它不是 size_t?”
    • @JeremyRodi:不确定,但我猜它使用int 而不是size_t,因为它是为了向后兼容在C 语言标准化之前编写的代码,在@ 类型之前987654331@ 甚至存在。
    【解决方案3】:

    一个简单的解决方案就是使用 unformatted 输出:

    fwrite(x.data, 1, x.len, stdout);
    
    这实际上是一种不好的形式,因为 `fwrite` 可能不会写所有东西,所以它应该在循环中使用;
    for (size_t i, remaining = x.len;
         remaining > 0 && (i = fwrite(x.data, 1, remaining, stdout)) > 0;
         remaining -= i) {
    }
    

    (编辑:fwrite 确实会在成功时写入整个请求范围;不需要循环。)

    确保x.len 不大于SIZE_T_MAX

    【讨论】:

    • +1 正准备放弃这个。很难变得更简单。
    • 但是如果我想使用类似 snprintf 的东西,这也行不通。虽然在这种情况下我可以只复制数据,但我宁愿能够使用其他格式说明符(如 %i)。
    • @drderp:我不明白。你会如何使用snprintf?您可以使用memcpy 写入内存位置而不是文件...
    • @KerrekSB 如果我想创建一个字符串和一个数字,我必须先使用memcpy 复制字符串,然后使用snprintf 输出数字我想要的位置。不如一口气做完,我认为...
    • @drderp:是的。好吧,这取决于您是否要处理带有空字节的任意数据。如果没有,最好使用%.*s 进行格式化打印。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-18
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    相关资源
    最近更新 更多