[C++11]_[初级]_[十六进制字符串转换为字节数组]

场景

  1. 在开发使用加密算法md5,sha256等的功能时, 会生成基于十六进制的字符串密钥。 这时候在使用这些密钥进行解密或加密的时候,第三方库都需要传入一个字节数组usigned char*格式的数组,它每个字节的高4位和低4位都分别存储了1个字符值。那么用C++如何实现?

说明

  1. 举例子比如字符串"a3fd",它有4个字符,那么它实际上只需要2个字节就可以存储。每两个字符存储在一个字节里,分别对应着高低4位。所以"a3fd"就是在内存里的结果是{0xa3,0xfd}

  2. 如果是使用python的话,bytes类型的对象默认就有fromhex方法,很方便,可惜C++标准库没有这么方便的方法。

    bytes.fromhex(strData)
    
  3. 我们知道标准字符串string可以存储任意的字节数据0~0xff, 它的size()并不是遇到\0就会结束。所以可以使用string来存储字节数组。

  4. C++11<string>里,增加了一个std::stoi方法,用来把字符串转换为对应的int值,也就是之前说的十六进制转换。 使用它来转换两个字符到int值,再把这个uint8_t值添加到作为字节数组存储的string里即可。

    int stoi( const std::string& str, std::size_t* pos = 0, int base = 10 );
    
  5. 注意,这里的十六进制字符串必须是偶数个,如果是奇数个,那么最有一个字符就不清楚是放在高位还是低位。

例子

  1. 这里分别用C++11C++98实现了转换的方法。如果是逆转过来,把字节数组转换为十六进制字符串,只需要使用stringstream作为输入或者sprintf "%.x"即可,参考ConvertBytesToEvenHexString()函数.
// test-hex-string-to-byte.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <stdint.h>
#include <iostream>
#include <assert.h>
#include <sstream>
#include <math.h>

using namespace std;

// https://blog.csdn.net/infoworld/article/details/9451591
static uint8_t stoi98(const string& data)
{
	auto& first = data[0];
	uint8_t f = (first & 0x40)?(first&0x0F)+0x9:(first & 0x0F);

	auto& second = data[1];
	uint8_t s = (second & 0x40)?(second&0x0F)+0x9:(second & 0x0F);

	return ((f << 4) & 0xf0) | (s & 0x0f);
}

static string ConvertEvenHexStringToBytesCpp11(const string& data)
{
	string result;
	auto size = data.size();
	auto count = size / 2;
	for(size_t i = 0; i< count; i++){
		auto value = (uint8_t)stoi(data.substr(i*2,2),0,16);
		result.append(1,value);
	}

	return result;
}

static string ConvertEvenHexStringToBytesCpp98(const string& data)
{
	string result;
	auto size = data.size();
	auto count = size / 2;
	for(size_t i = 0; i< count; i++){
		auto sub = data.substr(i*2,2);
		auto value = (uint8_t)stoi98(sub.c_str());
		result.append(1,value);
	}

	return result;
}

static string ConvertBytesToEvenHexString(const string& data)
{
	stringstream ss;
	ss << hex;
	for(size_t i = 0; i< data.size(); ++i){
		auto& one = data[i];
		ss << (one >> 4 & 0x0f) << (one & 0x0f);
	}

	string result = ss.str();
	return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
	auto str = "a3fd";
	auto result = ConvertEvenHexStringToBytesCpp11(str);
	auto mResult = "\xa3\xfd";
	assert(result.size() == (strlen(str) /2) );
	assert(result == mResult);

	auto source = ConvertBytesToEvenHexString(result);
	cout << source << endl;
	assert(str == source);

	str = "a3fd7be21c504d8ed7d19b";
	result = ConvertEvenHexStringToBytesCpp11(str);
	mResult = "\xa3\xfd\x7b\xe2\x1c\x50\x4d\x8e\xd7\xd1\x9b";
	assert(result.size() == (strlen(str) /2));
	assert(result == mResult);

	source = ConvertBytesToEvenHexString(result);
	cout << source << endl;
	assert(str == source);

	str = "a3fd";
	result = ConvertEvenHexStringToBytesCpp98(str);
	mResult = "\xa3\xfd";
	assert(result.size() == (strlen(str) /2));
	assert(result == mResult);

	source = ConvertBytesToEvenHexString(result);
	cout << source << endl;
	assert(str == source);

	str = "a3fd7be21c504d8ed7d19b";
	result = ConvertEvenHexStringToBytesCpp98(str);
	mResult = "\xa3\xfd\x7b\xe2\x1c\x50\x4d\x8e\xd7\xd1\x9b";
	assert(result.size() == (strlen(str) /2));
	assert(result == mResult);

	source = ConvertBytesToEvenHexString(result);
	cout << source << endl;
	assert(str == source);

	system("pause");
	return 0;
}

输出

a3fd
a3fd7be21c504d8ed7d19b
a3fd
a3fd7be21c504d8ed7d19b

参考

  1. 十六进制字符串转换数值并写入文件

  2. 输出内存数据的二进制和十六进制的字符串表示

  3. 使用zlib库压缩和解压字符串STL string

  4. C++11语言特性和标准库-第一部-infoworld的课程