【问题标题】:printf with %s to include null charactersprintf 与 %s 包含空字符
【发布时间】:2011-05-23 06:36:48
【问题描述】:

我需要连接一些字符串,并且需要包含 NULL 字节。我不想将 '\0' 视为终止字节。我想保存我宝贵的 NULL 字节!

在代码示例中,如果

    char *a = "\0hey\0\0";

我需要 printf 以输出“\0hey\0\0”的格式。

-奥斯汀

【问题讨论】:

  • 你将如何计算字符串的长度?
  • 其预先确定的。我已经分配了空间,比如 8 个字节。我想使用 printf(%s%s%s...) 将一些不同的给定大小的字符串拼接在一起
  • 如果数组嵌入了空值,则它不是字符串。不要称它为字符串。将其称为(无符号)字符数组(带有嵌入的空值)。由于它不是字符串,因此您无法在该对象上可靠地使用任何 string 函数:没有"%s" 转换为printf(或scanf),没有fputs,没有fgets ,没有strcpy,没有strcat,...

标签: c string printf


【解决方案1】:

怎么样:

int i;
for(i = 0; i < 4; i++)
    printf("%c", a[i]);

如果您希望在格式字符串中指定%s 时使用“类似printf”的函数,您可以在您自己的函数中包含上述代码。但是正如@Neil 所提到的,您将很难找到一种替代方法来寻找空字节来确定字符串的长度。为此,我想您可以使用某种转义字符。

【讨论】:

  • 为什么需要大量代码?这是一个微不足道的for循环吗?很难找到替代方案。
  • 我说了很多代码,因为对 10 个字符串执行此操作意味着 10 个 for 循环。当然不是世界末日。它似乎不太理想,但我承认我没有在最理想的情况下工作
  • @austin - 这不是说一个函数和十个调用吗?
  • 我建议为此使用 putchar() 而不是 printf()。字符不需要格式化,所以 putchar() 效率更高。
  • 或者fwrite(a, 1, 4, stdout);怎么样。这比putchar 还要好;您根本不需要一次遍历一个字节。
【解决方案2】:

这里的问题是字符串a 的长度不容易确定。比如你的代码..

char *a = "\0hey\0\0";

.. 为字符串分配七个字节,最后一个是 NULL 终止符。使用像 strlen 这样的函数会返回 0。

如果你知道字符串的精确长度,那么你可以这样写或迭代字节:

#ifdef ESCAPE_NULLS
    int i;
    for (i = 0; i <= 6; i++)
        if (a[i] == 0)
            printf("\\0");
        else
            printf("%c", a[i]);
#else
    write(1, a, 6);
#endif

但你必须了解 6。

另一种方法是不使用以 NULL 结尾的字符串,而是为您的字节实现另一种存储机制;例如一个长度编码的数组。

#include <stdio.h>

typedef struct {
    int length;
    char *bytes;
} bytearr;

void my_printf(bytearr *arr)
{
    #ifdef ESCAPE_NULLS
        int i;
        for (i = 0; i <= arr->length; i++)
            if (arr->bytes[i] == 0)
                printf("\\0");
            else
                printf("%c", arr->bytes[i]);
    #else
        write(1, arr->bytes, arr->length);
    #endif
}

void main(void)
{
    bytearr foo = {
        6, "\0hey\0\0"
    };
    my_printf(&foo);
}

不优雅,但希望你能明白。

编辑:2011-05-31

重读我刚刚注意到“连接”一词的问题。如果要将 NULL 字符从内存中的一个位置忠实地复制到另一个位置(而不是反斜杠转义),并且您事先知道每个数组中的字节总数,那么您可以简单地使用 memcpy

#include <string.h>
char *a = "\0hey\0\0";   /*  6 */
char *b = "word\0up yo"; /* 10 */
char *c = "\0\0\0\0";    /*  4 */
void main(void)
{
    char z[20];
    char *zp = z;
    zp = memcpy(zp, a, 6);
    zp = memcpy(zp, b, 10);
    zp = memcpy(zp, c, 4);
    /* now z contains all 20 bytes, including 8 NULLs */
}

【讨论】:

  • 您在回复的第一部分中有一个 if/else 语句。这是否意味着 NULL 字节上的 printf("%c") 不起作用?
  • 这取决于您是想忠实地将空字节传递给 STDOUT,还是反斜杠转义它(我是这样阅读原始问题的)。
  • @MattyK:不应该write(1, a, 6);bewrite(1, a, sizeof(char)*6);
  • 当然,如果您正在编写适当的、可移植的代码。很容易偷懒并假设sizeof(char)==1
  • 不要将 POSIX write() 与 stdio printf 混合使用。使用fwrite。并且不要将printf("%c" 用于单字节,使用putchar!或者更好的是,搜索下一个0 并使用fwrite 通过一个函数调用获取所有非零字节。或者复制并转义到本地缓冲区并 fwrite 。顺便说一句,memcpy 不是strcatmemcpy 返回未修改的第一个参数,而不是它写入的 end。因此,您的更新会反复覆盖 z 的开头。但是,是的,如果您使用正确的目标指针,memcpy 是完成这项工作的正确工具。
【解决方案3】:
char *a="\0hey\0\0";
int alen = 7;

char buf[20] = {0};
int bufSize = 20;

int i=0;
int j=0;
while( i<bufSize && j<alen )
{
    if(a[j]=='\0') {
        buf[i++]='\\';
        buf[i++]='0';
        j++;
    }
    else {
        buf[i++] = a[j++];
    }
}

printf(buf);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    相关资源
    最近更新 更多