简体   繁体   English

如何将十六进制字符串转换为C中的二进制字符串

[英]How to convert a hexadecimal string to a binary string in C

I have a text file with hexadecimal values.我有一个带有十六进制值的文本文件。 Now I need to convert the hexadecimal value to binary and need to save it on another file.现在我需要将十六进制值转换为二进制值并需要将其保存在另一个文件中。 But I don't know how to convert the hexadecimal value to a binary string!但是我不知道如何将十六进制值转换为二进制字符串! Please help...请帮忙...

It's quite easy, really, because the translation goes digit-by-digit.这真的很容易,因为翻译是逐位进行的。

0 - 0000
1 - 0001
2 - 0010
3 - 0011
4 - 0100
5 - 0101
6 - 0110
7 - 0111
8 - 1000
9 - 1001
A - 1010
B - 1011
C - 1100
D - 1101
E - 1110
F - 1111

So, for example, the hex number FE2F8 will be 11111110001011111000 in binary因此,例如,十六进制数FE2F8将是11111110001011111000二进制

const char input[] = "..."; // the value to be converted
char res[9]; // the length of the output string has to be n+1 where n is the number of binary digits to show, in this case 8
res[8] = '\0';
int t = 128; // set this to s^(n-1) where n is the number of binary digits to show, in this case 8
int v = strtol(input, 0, 16); // convert the hex value to a number

while(t) // loop till we're done
{
    strcat(res, t < v ? "1" : "0");
    if(t < v)
        v -= t;
    t /= 2;
}
// res now contains the binary representation of the number

As an alternative (this assumes there's no prefix like in "0x3A" ):作为替代方案(假设没有像"0x3A"那样的前缀):

const char binary[16][5] = {"0000", "0001", "0010", "0011", "0100", ...};
const char digits = "0123456789abcdef";

const char input[] = "..." // input value
char res[1024];
res[0] = '\0';
int p = 0;

while(input[p])
{
    const char *v = strchr(digits, tolower(input[p++]));
    if (v)
        strcat(res, binary[v - digits]);
}
// res now contains the binary representation of the number
void hex_binary(char * res){
char binary[16][5] = {"0000", "0001", "0010", "0011", "0100", "0101","0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110","1111"};
char digits [] = "0123456789abcdef";

const char input[] = "a9e6"; // input value
res[0] = '\0';
int p = 0;
int value =0;
    while(input[p])
    {
        const char *v = strchr(digits, tolower(input[p]));
        if(v[0]>96){
            value=v[0]-87;
        }
        else{
            value=v[0]-48;
        }
        if (v){
            strcat(res, binary[value]);
        }
        p++;
    }
    printf("Res:%s\n", res);
}

There are many ways to solve this question that use some arithmetics to convert from ascii character ranges 0-9 and af (or AF) to binary.有很多方法可以解决这个问题,即使用一些算术将 ascii 字符范围 0-9 和 af(或 AF)转换为二进制。 I wanted to find a solution which only uses a lookup table and benchmark that against a solution that uses arithmetics instead.我想找到一个仅使用查找表和基准的解决方案,该解决方案与使用算术的解决方案相反。 Strangly enough, none of the answers above implement a purely arithmetic solution and some answers even assume that "converting to binary" means converting to a ascii string of characters "0" and "1".奇怪的是,上面的答案都没有实现纯算术解决方案,有些答案甚至假设“转换为二进制”意味着转换为字符“0”和“1”的 ascii 字符串。

Lets first do some setups.让我们先做一些设置。 Firstly, we want to have the whole test data in memory so that we avoid disk I/O influencing the test.首先,我们希望将整个测试数据保存在内存中,以避免磁盘 I/O 影响测试。 Here is how I create a header with a character array "testdata" of 104857600 bytes, roughly 105 MB.下面是我如何创建一个带有 104857600 字节(大约 105 MB)字符数组“testdata”的标头。 As the question was how to convert files, our implementation should be fast on large data.由于问题是如何转换文件,我们的实现应该在大数据上很快。

$ { printf "char *testdata =\""; cat /dev/urandom \
    | tr -d -c "0123456789abcdefABCDEF" \
    | dd count=100 iflag=fullblock bs=1M; printf "\";\n" } > testdata.h

Next, we create the lookup tables.接下来,我们创建查找表。 I see two possible ways to solve this with a lookup table.我看到有两种可能的方法可以通过查找表解决这个问题。 Either the lookup table maps individual ascii hex characters to half bytes or it maps two hex characters to a full byte.查找表要么将单个 ascii 十六进制字符映射到半字节,要么将两个十六进制字符映射到一个完整字节。 In the former case, the lookup table has to have 256 entries.在前一种情况下,查找表必须有 256 个条目。 In the latter case, the lookup table has to have 256*256=65536 entries.在后一种情况下,查找表必须有 256*256=65536 个条目。 We can reduce the size of the latter by realizing that the first bit of the first byte will never be used.我们可以通过意识到永远不会使用第一个字节的第一位来减小后者的大小。 So we only need a lookup table of 128*256=32768 entries.所以我们只需要一个 128*256=32768 个条目的查找表。 Since that solution also requires an additional calculation step (applying a bitmask) we will benchmark both.由于该解决方案还需要额外的计算步骤(应用位掩码),我们将对两者进行基准测试。 We end up with the following test cases:我们最终得到以下测试用例:

  1. arithmetic solution算术解
  2. 256 entries lookup table 256个条目查找表
  3. 32768 entries lookup table 32768个条目查找表
  4. 65536 entries lookup table 65536个条目查找表

The first lookup table is easy to generate using some python:第一个查找表很容易使用一些python生成:

#!/usr/bin/env python

import sys,struct

sys.stdout.write("unsigned char base16_decoding_table1[256] = {\n")

for i in xrange(256):
    try:
        j = str(int(chr(i), 16))
    except:
        j = '0'
    sys.stdout.write(j+',')
sys.stdout.write("};\n")

sys.stdout.write("\n")

l = 128*256*["0"]

for a in ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F']:
    for b in ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F']:
        l[struct.unpack("<H", a+b)[0]] = str(int(a+b, 16))

line = "unsigned char base16_decoding_table2[%d] = {"%(128*256)

for e in l:
    line += e+","
    if len(line) > 70:
        sys.stdout.write(line+"\n")
        line = ""
sys.stdout.write(line+"};\n")

sys.stdout.write("\n")

l = 256*256*["0"]

for a in ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F']:
    for b in ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F']:
        l[struct.unpack("<H", a+b)[0]] = str(int(a+b, 16))

line = "unsigned char base16_decoding_table3[%d] = {"%(256*256)

for e in l:
    line += e+","
    if len(line) > 70:
        sys.stdout.write(line+"\n")
        line = ""
sys.stdout.write(line+"};\n")

And then:进而:

python gen.py > base16_decoding_table.h

Now we can write some C code to test.现在我们可以编写一些 C 代码进行测试。

#include <stdio.h>
#include <time.h>
#include <inttypes.h>

#include "testdata.h"
#include "base16_decoding_table.h"

#define TESTDATALEN 104857600

/* the resulting binary string is half the size of the input hex string
 * because every two hex characters map to one byte */
unsigned char result[TESTDATALEN/2];

void test1()
{
    size_t i;
    char cur;
    unsigned char val;
    for (i = 0; i < TESTDATALEN; i++) {
        cur = testdata[i];
        if (cur >= 97) {
            val = cur - 97 + 10;
        } else if (cur >= 65) {
            val = cur - 65 + 10;
        } else {
            val = cur - 48;
        }
        /* even characters are the first half, odd characters the second half
         * of the current output byte */
        if (i%2 == 0) {
            result[i/2] = val << 4;
        } else {
            result[i/2] |= val;
        }
    }
}

void test2()
{
    size_t i;
    char cur;
    unsigned char val;
    for (i = 0; i < TESTDATALEN; i++) {
        cur = testdata[i];
        val = base16_decoding_table1[(int)cur];
        /* even characters are the first half, odd characters the second half
         * of the current output byte */
        if (i%2 == 0) {
            result[i/2] = val << 4;
        } else {
            result[i/2] |= val;
        }
    }
}

void test3()
{
    size_t i;
    uint16_t *cur;
    unsigned char val;
    for (i = 0; i < TESTDATALEN; i+=2) {
        cur = (uint16_t*)(testdata+i);
        // apply bitmask to make sure that the first bit is zero
        val = base16_decoding_table2[*cur & 0x7fff];
        result[i/2] = val;
    }
}

void test4()
{
    size_t i;
    uint16_t *cur;
    unsigned char val;
    for (i = 0; i < TESTDATALEN; i+=2) {
        cur = (uint16_t*)(testdata+i);
        val = base16_decoding_table3[*cur];
        result[i/2] = val;
    }
}

#define NUMTESTS 1000

int main() {
    struct timespec before, after;
    unsigned long long checksum;
    int i;
    double elapsed;

    clock_gettime(CLOCK_MONOTONIC, &before);
    for (i = 0; i < NUMTESTS; i++) {
        test1();
    }
    clock_gettime(CLOCK_MONOTONIC, &after);

    checksum = 0;
    for (i = 0; i < TESTDATALEN/2; i++) {
        checksum += result[i];
    }
    printf("checksum: %llu\n", checksum);
    elapsed = difftime(after.tv_sec, before.tv_sec) + (after.tv_nsec - before.tv_nsec)/1.0e9;
    printf("arithmetic solution took %f seconds\n", elapsed);

    clock_gettime(CLOCK_MONOTONIC, &before);
    for (i = 0; i < NUMTESTS; i++) {
        test2();
    }
    clock_gettime(CLOCK_MONOTONIC, &after);

    checksum = 0;
    for (i = 0; i < TESTDATALEN/2; i++) {
        checksum += result[i];
    }
    printf("checksum: %llu\n", checksum);
    elapsed = difftime(after.tv_sec, before.tv_sec) + (after.tv_nsec - before.tv_nsec)/1.0e9;
    printf("256 entries table took %f seconds\n", elapsed);

    clock_gettime(CLOCK_MONOTONIC, &before);
    for (i = 0; i < NUMTESTS; i++) {
        test3();
    }
    clock_gettime(CLOCK_MONOTONIC, &after);

    checksum = 0;
    for (i = 0; i < TESTDATALEN/2; i++) {
        checksum += result[i];
    }
    printf("checksum: %llu\n", checksum);
    elapsed = difftime(after.tv_sec, before.tv_sec) + (after.tv_nsec - before.tv_nsec)/1.0e9;
    printf("32768 entries table took %f seconds\n", elapsed);

    clock_gettime(CLOCK_MONOTONIC, &before);
    for (i = 0; i < NUMTESTS; i++) {
        test4();
    }
    clock_gettime(CLOCK_MONOTONIC, &after);

    checksum = 0;
    for (i = 0; i < TESTDATALEN/2; i++) {
        checksum += result[i];
    }
    printf("checksum: %llu\n", checksum);
    elapsed = difftime(after.tv_sec, before.tv_sec) + (after.tv_nsec - before.tv_nsec)/1.0e9;
    printf("65536 entries table took %f seconds\n", elapsed);

    return 0;
}

Lets compile the thing:让我们编译一下:

$ gcc -O3 -g -Wall -Wextra test.c

And run it:并运行它:

$ ./a.out

The result:结果:

  1. arithmetic solution: 437.17 s算术解决方案: 437.17 秒
  2. 256 entries lookup table: 117.80 s 256 个条目查找表: 117.80 秒
  3. 32768 entries lookup table: 52.33 s 32768 个条目查找表: 52.33 秒
  4. 65536 entries lookup table: 44.66 s 65536 个条目查找表: 44.66 秒

This we can conclude lookup tables beat arithmetic solutions any time and that wasting memory for bigger lookup tables might be worth the additional runtime.我们可以得出结论,查找表在任何时候都胜过算术解决方案,并且为更大的查找表浪费内存可能值得额外的运行时间。

void printBin(unsigned int num){
  char str[sizeof(num)*8];
  char *p = str;
  for(*p='0'; num; num/=2) { *p++='0'+num%2; } //store remainders
  for(--p; p>=str; putchar(*p--)) {;}          //print remainders in reverse
  putchar('\n');
}

That's my function to convert HEX to BIN, byte a byte.这是我将 HEX 转换为 BIN 的功能,一个字节一个字节。

void HexToBin(char hex_number, char* bit_number) {
    int max = 128;
    for(int i = 7 ; i >-1 ; i--){
        bit_number [i] = (hex_number & max ) ? 1 : 0;
        max >>=1;
    }
}

and the call to the function:以及对函数的调用:

void main (void){

    char hex_number = 0x6E; //0110 1110
    char bit_number[8]={0,0,0,0,0,0,0,0};
    HexToBin(hex_number,bit_number);

    for(int i = 7 ; i >-1 ; i--)
        printf("%d",bit_number[i]);

    printf("\n");
    system("pause");
}

And here is the MSDOS answer:这是 MSDOS 的答案:

01101110

Press a key to continue . . .

Quite easy!很简单!

The quickest and easiest way is to read the hex file and, for each character ('0' through 'F') read, do a table lookup of the equivalent (0 through 15) binary value.最快和最简单的方法是读取十六进制文件,对于读取的每个字符('0' 到 'F'),执行等效(0 到 15)二进制值的表查找。 There are, as ever, more elegant ways but this is very straightforward, maybe something like:一如既往,有更优雅的方式,但这是非常简单的,可能是这样的:

switch (charval) {
  case '0': binval = 0;
  case '1': binval = 1;
  case '2': binval = 2;
  case '3': binval = 3;
   ....
  case 'a': binval = 10;
  case 'b': binval = 11;
  case 'A': binval = 10;
  case 'B': binval = 11;
  ....
  case 'f':  binval = 15;
  case 'F':  binval = 15;
  default:   binval = -1;  // error case
}

Now you have to use shifts and IORs/ADDs to construct words of the size you want from these individual 4-bit binary values.现在,您必须使用移位和 IOR/ADD 从这些单独的 4 位二进制值中构建您想要的大小的字。

#include <stdio.h>

int main()
{
    long int binaryNumber,
             hexadecimalNumber = 0,
             j = 1,
             remainder;

    printf("Enter any number any binary number: ");
    scanf("%ld", &binaryNumber);

    while(binaryNumber != 0) {
        remainder = binaryNumber % 10;
        hexadecimalNumber = hexadecimalNumber + remainder * j;
        j = j * 2;
        binaryNumber = binaryNumber / 10;
    }
    printf("Equivalent hexadecimal value: %X", hexadecimalNumber);
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM