当前位置:  开发笔记 > 前端 > 正文

是否有printf转换器以二进制格式打印?

如何解决《是否有printf转换器以二进制格式打印?》经验,为你挑选了16个好方法。

我可以用printf作为十六进制或八进制数打印.是否有格式标记打印为二进制或任意基数?

我正在运行gcc.

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"

William Whyt.. 248

Hacky但对我有用:

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0') 
printf("Leading text "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(byte));

对于多字节类型

printf("m: "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN"\n",
  BYTE_TO_BINARY(m>>8), BYTE_TO_BINARY(m));

不幸的是,你需要所有额外的报价.这种方法具有宏的效率风险(不要将函数作为参数传递BYTE_TO_BINARY),但在此处的一些其他提议中避免了内存问题和strcat的多次调用.



1> William Whyt..:

Hacky但对我有用:

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0') 
printf("Leading text "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(byte));

对于多字节类型

printf("m: "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN"\n",
  BYTE_TO_BINARY(m>>8), BYTE_TO_BINARY(m));

不幸的是,你需要所有额外的报价.这种方法具有宏的效率风险(不要将函数作为参数传递BYTE_TO_BINARY),但在此处的一些其他提议中避免了内存问题和strcat的多次调用.


并且还有一个优点是可以在`printf`中多次调用,而`static`缓冲区则不能.
我冒昧地将`%d`改为`%c`,因为它应该更快('%d`必须执行数字 - >字符转换,而'%c`只是输出参数
发布了这个宏的扩展版本,支持16,32,64位int:http://stackoverflow.com/a/25108449/432509
请注意,此方法不适合堆栈.假设`int`在系统上是32位,打印单个32位值将需要32*4字节值的空间; 总共128个字节.根据堆栈大小,这可能是也可能不是问题.

2> 小智..:

打印任何数据类型的二进制文件

//assumes little endian
void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    int i, j;

    for (i=size-1;i>=0;i--)
    {
        for (j=7;j>=0;j--)
        {
            byte = (b[i] >> j) & 1;
            printf("%u", byte);
        }
    }
    puts("");
}

测试

int main(int argv, char* argc[])
{
        int i = 23;
        uint ui = UINT_MAX;
        float f = 23.45f;
        printBits(sizeof(i), &i);
        printBits(sizeof(ui), &ui);
        printBits(sizeof(f), &f);
        return 0;
}


建议`size_t i; for(i = size; i--> 0;)`以避免`size_t`与`int`不匹配.
@ ZX9对你来说仍然是一个有用的原始注释,因为编码器确实需要小心,考虑边缘情况下使用`>`和`> =`和无符号类型."0"是无符号边缘情况,通常发生,与带有不太常见的"INT_MAX/INT_MIN"的带符号数学不同.
取'ptr`中的每个字节(外循环); 然后对于每个位当前字节(内部循环),用当前位("1 << j`)掩码字节.向右移动产生一个包含0('0000 0000b`)或1('0000 0001b`)的字节.以格式为'%u`打印生成的字节printf.HTH.

3> EvilTeach..:

这是一个快速的黑客,以演示做你想要的技术.

#include       /* printf */
#include      /* strcat */
#include      /* strtol */

const char *byte_to_binary(int x)
{
    static char b[9];
    b[0] = '\0';

    int z;
    for (z = 128; z > 0; z >>= 1)
    {
        strcat(b, ((x & z) == z) ? "1" : "0");
    }

    return b;
}

int main(void)
{
    {
        /* binary string to int */

        char *tmp;
        char *b = "0101";

        printf("%d\n", strtol(b, &tmp, 2));
    }

    {
        /* byte to binary string */

        printf("%s\n", byte_to_binary(5));
    }

    return 0;
}


一些变化:`strcat`是一种在循环的每次传递中向字符串添加单个char的低效方法.相反,添加一个`char*p = b;`并用`*p ++ =(x&z)替换内循环?'1':'0'.`z`应该从128(2 ^ 7)而不是256(2 ^ 8)开始.考虑更新以获取指向要使用的缓冲区的指针(用于线程安全),类似于`inet_ntoa()`.
此外,这应该记录在再次调用函数之前先前的结果将无效,因此调用者不应该尝试使用它:`printf("%s +%s =%s",byte_to_binary(3),byte_to_binary( 4),byte_to_binary(3 + 4))`.
@EvilTeach:你自己使用三元运算符作为`strcat()`的参数!我同意`strcat`可能比后递增一个用于赋值的解引用指针更容易理解,但即使是初学者也需要知道如何正确使用标准库.使用索引数组进行赋值可能是一个很好的演示(并且实际上可以工作,因为每次调用函数时,`b`都不会重置为全零).
随机:二进制缓冲区char是静态的,并在分配中清除为全零.这只会在它第一次运行时清除它,之后它不会清除,而是使用最后一个值.
这肯定不如自定义为printf编写转义重载那么"怪异".对于不熟悉代码的开发人员来说,这很容易理解.

4> DGentry..:

通常,glibc中没有二进制转换说明符.

可以将自定义转换类型添加到glibc中的printf()函数系列中.有关详细信息,请参阅register_printf_function.您可以添加自定义%b转换以供自己使用,如果它简化了应用程序代码以使其可用.

以下是如何在glibc中实现自定义printf格式的示例.



5> Shahbaz..:

您可以使用小桌子来提高速度1.类似的技术在嵌入式世界中很有用,例如,反转一个字节:

const char *bit_rep[16] = {
    [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
    [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
    [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
    [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
};

void print_byte(uint8_t byte)
{
    printf("%s%s", bit_rep[byte >> 4], bit_rep[byte & 0x0F]);
}

1我主要指的是优化器不那么激进且速度差异可见的嵌入式应用程序.



6> danijar..:

打印最低位并将其移出右侧.执行此操作直到整数变为零将打印二进制表示而不会引导零,但按相反的顺序排列.使用递归,订单可以很容易地纠正.

#include 

void print_binary(int number)
{
    if (number) {
        print_binary(number >> 1);
        putc((number & 1) ? '1' : '0', stdout);
    }
}

对我而言,这是解决问题的最简洁的解决方案之一.如果你喜欢0b前缀和尾随的新行字符,我建议包装该函数.

在线演示


您还应该使用unsigned int数字,因为当给定数字为负数时,该函数将输入永无休止的递归调用。

7> ideasman42..:

基于@William怀特的回答,这是提供了一个宏int8,16,3264版本,再使用INT8宏来避免重复.

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16             PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32             PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include 
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

这输出:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

为了便于阅读,您可能需要添加一个分隔符,例如:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000


您如何建议添加逗号?

8> R....:

这是一个不受重入问题影响的函数版本或对参数大小/类型的限制:

#define FMT_BUF_SIZE (CHAR_BIT*sizeof(uintmax_t)+1)
char *binary_fmt(uintmax_t x, char buf[static FMT_BUF_SIZE])
{
    char *s = buf + FMT_BUF_SIZE;
    *--s = 0;
    if (!x) *--s = '0';
    for(; x; x/=2) *--s = '0' + x%2;
    return s;
}

请注意,如果只是用所需的基数替换2,那么此代码对于2到10之间的任何基数都可以正常工作.用法是:

char tmp[FMT_BUF_SIZE];
printf("%s\n", binary_fmt(x, tmp));

哪里x是积分表达式.


这仍然是一个糟糕的设计.在这些情况下,它需要额外的复制步骤,并且即使在不需要复制的情况下,它也不会比调用者提供缓冲区贵**.使用静态存储只是一个不好的习惯.
是的,你可以这样做.但这是非常糟糕的设计.即使您没有线程或重入,调用者也必须知道静态缓冲区正在被重用,并且`char*a = binary_fmt(x),*b = binary_fmt(y);`之类的东西不会按预期工作.强制调用者传递缓冲区使存储需求明确; 如果确实需要,调用者当然可以自由地使用静态缓冲区,然后重用相同的缓冲区变得明确.另请注意,在现代PIC ABI上,静态缓冲区的访问代码通常比堆栈上的缓冲区要多.
在这里,我们不得不反对.我看不出如何添加一个不显眼的预处理器符号来限制使用情况严重,使得界面容易出错,保留永久存储的程序期间的临时值,并在最糟糕的生成代码的危害性附近的任何地方现代平台.
我没有提倡无理由的微优化(即测量).但我确实认为性能,即使是在微增益范围内,也值得一提,当它作为奖励以及基本优越的设计时.
必须使用必要的额外名称污染预处理程序或变量符号表的名称空间,必须使用该名称来正确调整_every_ caller必须分配的存储空间,并强制每个调用者知道此值并分配必要的数量存储,当简单的功能 - 本地存储解决方案足以满足大多数意图和目的时,以及当简单的strdup()调用覆盖其余99%的使用时,设计是不好的.
我建议使用堆栈存储.让调用者为此调用`malloc`会非常丑陋; 它不仅浪费大量时间和内存,而且最糟糕的是,你必须处理错误案例.我的意图始终是大多数调用者都会将指针传递给具有自动存储持续时间的数组.请注意,使用C99,它甚至可以是在调用时创建的复合文字.
在许多情况下,重入并不重要.
另外,你所说的wrt顺序处理多个结果本身并不是重入,而是简单地使用相当于全局对象来存储结果的余量.该函数不会被重新输入.在C中,用于处理将结果存储在全局对象中的函数的正确或至少广泛使用的习惯是在获得它们时立即复制这些结果.这具有的主要优点是,如果一次只需要一个结果,则不需要额外的分配.

9> 小智..:
const char* byte_to_binary( int x )
{
    static char b[sizeof(int)*8+1] = {0};
    int y;
    long long z;
    for (z=1LL<0; z>>=1,y++)
    {
        b[y] = ( ((x & z) == z) ? '1' : '0');
    }

    b[y] = 0;

    return b;
}


如果在三元组中使用"1"和"0"而不是"49"和"48",则更清楚.此外,`b`应该是9个字符长,所以最后一个字符可以保持为空终止符.
如果你更改了一些内容:1.为最后的零增加空间:`static char b [9] = {0}`2.将声明移出循环:`int z,y;`3.添​​加最后的零: `b [y] = 0`.这样就不需要重新焕发活力了.

10> TechplexEngi..:

之前发布的答案都不是我想要的,所以我写了一个.使用%B非常简单printf!

    /*
     * File:   main.c
     * Author: Techplex.Engineer
     *
     * Created on February 14, 2012, 9:16 PM
     */

    #include 
    #include 
    #include 
    #include 
    #include 


    static int printf_arginfo_M(const struct printf_info *info, size_t n, int *argtypes) {
        /* "%M" always takes one argument, a pointer to uint8_t[6]. */
        if (n > 0) {
            argtypes[0] = PA_POINTER;
        }
        return 1;
    } /* printf_arginfo_M */

    static int printf_output_M(FILE *stream, const struct printf_info *info, const void *const *args) {
        int value = 0;
        int len;

        value = *(int **) (args[0]);

        //Beginning of my code ------------------------------------------------------------
        char buffer [50] = ""; //Is this bad?
        char buffer2 [50] = ""; //Is this bad?
        int bits = info->width;
        if (bits <= 0)
            bits = 8; // Default to 8 bits

        int mask = pow(2, bits - 1);
        while (mask > 0) {
            sprintf(buffer, "%s", (((value & mask) > 0) ? "1" : "0"));
            strcat(buffer2, buffer);
            mask >>= 1;
        }
        strcat(buffer2, "\n");
        // End of my code --------------------------------------------------------------
        len = fprintf(stream, "%s", buffer2);
        return len;
    } /* printf_output_M */

    int main(int argc, char** argv) {

        register_printf_specifier('B', printf_output_M, printf_arginfo_M);

        printf("%4B\n", 65);

        return (EXIT_SUCCESS);
    }


警告:这仅适用于glibc用户!

11> rlerallut..:

一些运行时支持"%b",尽管这不是标准.

另见这里有趣的讨论:

http://bytes.com/forum/thread591027.html

HTH



12> mrwes..:

此代码应满足您最多64位的需求.我创建了2个函数pBin&pBinFill.两者都做同样的事情,但是pBinFill用fillChar填充前导空格.测试功能生成一些测试数据,然后使用该功能将其打印出来.


char* pBinFill(long int x,char *so, char fillChar); // version with fill
char* pBin(long int x, char *so);                   // version without fill
#define kDisplayWidth 64

char* pBin(long int x,char *so)
{
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';  // determine bit
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 i++;   // point to last valid character
 sprintf(so,"%s",s+i); // stick it in the temp string string
 return so;
}
char* pBinFill(long int x,char *so, char fillChar)
{ // fill in array from right to left
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 while(i>=0) s[i--]=fillChar;    // fill with fillChar 
 sprintf(so,"%s",s);
 return so;
}
void test()
{
 char so[kDisplayWidth+1]; // working buffer for pBin
 long int val=1;
 do
 {
   printf("%ld =\t\t%#lx =\t\t0b%s\n",val,val,pBinFill(val,so,'0'));
   val*=11; // generate test data
 } while (val < 100000000);
}

Output:
00000001 =  0x000001 =  0b00000000000000000000000000000001
00000011 =  0x00000b =  0b00000000000000000000000000001011
00000121 =  0x000079 =  0b00000000000000000000000001111001
00001331 =  0x000533 =  0b00000000000000000000010100110011
00014641 =  0x003931 =  0b00000000000000000011100100110001
00161051 =  0x02751b =  0b00000000000000100111010100011011
01771561 =  0x1b0829 =  0b00000000000110110000100000101001
19487171 = 0x12959c3 =  0b00000001001010010101100111000011


@mhambra:你应该告诉log4cxx使用像`width`这样的通用名称!

13> chux - Reins..:

是否有printf转换器以二进制格式打印?

printf()系列只能使用标准说明符直接在基数8,10和16中打印.我建议创建一个函数,根据代码的特定需要将数字转换为字符串.


在任何基地打印[2-36]

到目前为止,所有其他答案至少具有这些限制之一.

    使用静态内存作为返回缓冲区.这限制了函数可用作参数的次数printf().

    分配需要调用代码释放指针的内存.

    要求调用代码显式提供合适的缓冲区.

    printf()直接打电话.这迫使新的功能来fprintf(),sprintf(),vsprintf()等.

    使用减小的整数范围.

以下没有上述限制.它确实需要C99或更高版本并使用"%s".它使用复合文字来提供缓冲区空间.它在多个呼叫中没有问题printf().

#include 
#include 
#define TO_BASE_N (sizeof(unsigned)*CHAR_BIT + 1)

//                               v. compound literal .v
#define TO_BASE(x, b) my_to_base((char [TO_BASE_N]){""}, (x), (b))

// Tailor the details of the conversion function as needed
// This one does not display unneeded leading zeros
// Use return value, not `buf`
char *my_to_base(char *buf, unsigned i, int base) {
  assert(base >= 2 && base <= 36);
  char *s = &buf[TO_BASE_N - 1];
  *s = '\0';
  do {
    s--;
    *s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % base];
    i /= base;
  } while (i);

  // Could employ memmove here to move the used buffer to the beginning

  return s;
}

#include 
int main(void) {
  int ip1 = 0x01020304;
  int ip2 = 0x05060708;
  printf("%s %s\n", TO_BASE(ip1, 16), TO_BASE(ip2, 16));
  printf("%s %s\n", TO_BASE(ip1, 2), TO_BASE(ip2, 2));
  puts(TO_BASE(ip1, 8));
  puts(TO_BASE(ip1, 36));
  return 0;
}

产量

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44



14> Florian Bösc..:

C标准库中没有格式化函数来输出这样的二进制文件.printf系列支持的所有格式操作都是针对人类可读的文本.



15> quinmars..:

也许有点OT,但是如果你需要这个只是为了理解或回溯你正在做的一些二进制操作,你可以看看wcalc(一个简单的控制台计算器).使用-b选项可以获得二进制输出.

例如

$ wcalc -b "(256 | 3) & 0xff"
 = 0b11



16> kapil..:

以下递归函数可能很有用:

void bin(int n)
{
    /* Step 1 */
    if (n > 1)
        bin(n/2);
    /* Step 2 */
    printf("%d", n % 2);
}


注意,这不适用于负整数.
推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有