字节数组和short,int,float,double等类型的相互转换

一、在C++中从字节数组中获取short,int,long,float,double等数据

在进行Modbus协议通信和网络编程时,有时需要将从串口或者网络中接收的数据从字节数组转换成对应的int,float,double等数据,有时还要考虑大小端字节序以及Swap的问题,发现在C++中需要自己写相关的转换函数,于是/写了一个函数,用于从输入的byte数组中获取指定类型的数据,目前支持int16,int32,int64,float,double,对应的代码如下:

#ifndef _BYTECONVERTTOOLS_H
#define _BYTECONVERTTOOLS_H

#include <algorithm>
#include <array>
#include <regex>	// for std::regex_match
#include <iostream>

using namespace std;

// 自定义
typedef unsigned char		uint8;
typedef unsigned short		uint16;
typedef unsigned int		uint32;
#ifdef WIN32
typedef unsigned __int64	uint64;
typedef __int64	 int64;
#else
typedef unsigned long long	uint64;
typedef long long	int64;
#endif
typedef char	int8;
typedef short	int16;
typedef int		int32;

#include <string.h>

// 数组
#include <string>
#include <vector>
typedef std::string	String;
typedef std::vector<uint8>		Uint8Array;
typedef std::vector<uint16>		Uint16Array;
typedef std::vector<uint32>		Uint32Array;
typedef std::vector<uint64>		Uint64Array;
typedef std::vector<int8>		Int8Array;
typedef std::vector<int16>		Int16Array;
typedef std::vector<int32>		Int32Array;
typedef std::vector<int64>		Int64Array;
typedef std::vector<float>		Float32Array;
typedef std::vector<double>		Float64Array;
typedef std::vector<std::string>	StringArray;
typedef std::vector<Uint8Array> Uint8sArray;

namespace ByteConvertTools
{
	// 输入的byte数组中获取指定类型的数据
	// 支持int16,int32,int64,float,double
	template<typename T>
	bool get_data(T& _return, const uint8* buffer, size_t buffersize,
		uint16 offset_bytes, bool isLittle, bool isSwapByte)
	{
		uint32 totalByteNum = buffersize;
		uint32 byteNum = sizeof(T);
		uint32 regNum = byteNum / 2;
		uint32 startPos = offset_bytes;
		uint32 endPos = startPos + byteNum;
		if ((regNum == 0 || byteNum % 2 != 0) || (startPos > totalByteNum || endPos > totalByteNum)) {
			return false;
		}
		// 获取模板参数T的具体类型(int16,int32,int64,float,double)
		auto& type = typeid(T);
		if ((type == typeid(double) || type == typeid(int64) || type == typeid(uint64)) ||
			(type == typeid(float) || type == typeid(uint32) || type == typeid(int32)) ||
			(type == typeid(int16) || type == typeid(uint16))) {
			Uint8Array tmp8; Uint16Array tmp16(regNum);
			/*
				不同的计算机体系结构使用不同的字节顺序存储数据。
				“大端”表示最高有效字节在单词的左端。即最高位字节存放在字节数组的低位
				“小端”表示最高有效字节在单词的右端。即最高位字节存放在字节数组的高位
			*/
			if (isLittle) {
				// 小端字节序 dcba
				std::copy(buffer + startPos, buffer + endPos, std::back_inserter(tmp8));
			}
			else {
				// 大端字节序,则将字节数组进行反转 abcd
				std::reverse_copy(buffer + startPos, buffer + endPos, std::back_inserter(tmp8));
			}
			memcpy(tmp16.data(), tmp8.data(), byteNum);
			if (isSwapByte)
			{
				std::reverse(tmp16.begin(), tmp16.end());
				Uint8Array tmp1(byteNum);
				memcpy(tmp1.data(), tmp16.data(), byteNum);
				std::reverse(tmp1.begin(), tmp1.end());
				memcpy(tmp16.data(), tmp1.data(), byteNum);
			}
			memcpy(&_return, tmp16.data(), byteNum);
			return true;
		}
		return false;
	}

	template<typename T>
	bool get_data(T& _return, const Uint8Array& buffer,
		uint16 offset_bytes, bool isLittle, bool isSwapByte)
	{
		return get_data(_return, buffer.data(), buffer.size(), offset_bytes, isLittle, isSwapByte);
	}

	// 判断本台机器是大端字节序还是小端字节序
	bool isLittleEndian()
	{
		int iData = 1;
		char *p = (char*)&iData;
		if (*p == 1)
		{
			return true;
		}
		else {
			return false;
		}
	}

	template <typename T>
	void swapBytes(T& var)
	{
		static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
		std::array<char, sizeof(T)> varArray;
		std::memcpy(varArray.data(), &var, sizeof(T));
		for (int i = 0; i < static_cast<int>(sizeof(var) / 2); i++)
			std::swap(varArray[sizeof(var) - 1 - i], varArray[i]);
		std::memcpy(&var, varArray.data(), sizeof(T));
	}

	// 将int16,int32,int64,float,double等转换成16进制字节数组
	template<typename T>
	bool convertToBytesArray(Uint8Array& _return, const T value, bool isLittle, bool isSwapByte)
	{
		uint32 byteNum = sizeof(T);

		// 将T类型(int16,int32,int64,float,double等)的内容拷贝至tmp8中
		Uint8Array tmp8(byteNum);
		Uint16Array tmp16(byteNum / 2);

		bool isLocalMachineLittleEndian = isLittleEndian();

		if (isLittle == isLocalMachineLittleEndian)	// 如果是小端
		{
			memcpy(tmp8.data(), &value, byteNum);
		} 
		else {
			memcpy(tmp8.data(), &value, byteNum);
			// 将小端字节序转换成大端字节序或者将大端字节序转换成小端字节序
			std::reverse(tmp8.begin(), tmp8.end());
		}
		
		// 交换相邻的两个字节
		memcpy(tmp16.data(), tmp8.data(), byteNum);
		if (isSwapByte)
		{
			std::reverse(tmp16.begin(), tmp16.end());
			Uint8Array tmp1(byteNum);
			memcpy(tmp1.data(), tmp16.data(), byteNum);
			std::reverse(tmp1.begin(), tmp1.end());
			memcpy(tmp16.data(), tmp1.data(), byteNum);
		}
		memcpy(tmp8.data(), tmp16.data(), byteNum);

		_return = tmp8;

		return true;
	}

	// c++用正则表达式判断匹配字符串中的数字数值(包括负数,小数,整数)MFC编辑框判断数值
	static bool isStrNumber(String strInput)
	{
		//正则匹配数值包括负数 小数 整数
		std::string strPattern{ "-[0-9]+(.[0-9]+)?|[0-9]+(.[0-9]+)?" };
		std::regex re(strPattern);
		bool retMatchStr = false;

		retMatchStr = std::regex_match(strInput, re);

		return retMatchStr;
	}
};

#endif

main.cpp测试代码

#include <iostream>

#include "ByteConvertTools.h"

int main(int argc, char* argv[])
{
	/*	数据转换
		float 	3.14
		mem	    0xF5C3 0x4048
		mem 	C3 F5 48 40

		大端数据 + 大端传输
		transfer 	40 48 F5 C3
		convert 1	C3 F5 48 40

		小端数据
		传输	C3 F5 48 40

		大端swap
		传输	48 40 C3 F5	uint8[]
		convert 1	0x4048 0xF5C3	uint16[]
		0xF5C3 0x4048
		C3 F5 48 40	UINT8[]

		小端swap
		传输	F5 C3 40 48
		convert1	48 40 c3 f5
		0x4048 0xf5c3
		0xf5c3 0x4048
	*/

	/*
		不同的计算机体系结构使用不同的字节顺序存储数据。
		“大端”表示最高有效字节在单词的左端。 
		“小端”表示最高有效字节在单词的右端。
	*/

	//数据转换
	//float 	3.14
	//mem	    0xF5C3 0x4048
	//mem 		C3 F5 48 40

	// 小端数据
	// 传输: C3 F5 48 40
	float f1;
	uint8 bytesArr1[4] = { 0xC3, 0xF5, 0x48, 0x40 };
	ByteConvertTools::get_data(f1, bytesArr1, 4, 0, true, false);
	std::cout << "f1=" << f1 << std::endl;
	// f1: 3.14

	// 大端数据 + 大端传输
	// transfer 40 48 F5 C3
	// convert  C3 F5 48 40
	float f2;
	uint8 bytesArr2[4] = { 0x40, 0x48, 0xF5, 0xC3 };
	ByteConvertTools::get_data(f2, bytesArr2, 4, 0, false, false);
	std::cout << "f2=" << f2 << std::endl;
	// f2 : 3.14

	// 大端Swap
	// 传输: 48 40 C3 F5 uint8[]
	float f3;
	uint8 bytesArr3[4] = { 0x48, 0x40, 0xC3, 0xF5 };
	ByteConvertTools::get_data(f3, bytesArr3, 4, 0, false, true);
	std::cout << "f3=" << f3 << std::endl;
	// f3: 3.14

	/*小端swap
	传输	F5 C3 40 48
	convert1	48 40 c3 f5
	0x4048 0xf5c3
	0xf5c3 0x4048*/
	float f4;
	uint8 bytesArr4[4] = { 0xF5, 0xC3, 0x40, 0x48 };
	ByteConvertTools::get_data(f4, bytesArr4, 4, 0, true, true);
	std::cout << "f4=" << f4 << std::endl;
	// f4: 3.14

	return 0;
}

二、C#中字节数组和基本数据类型的相互转换

在C#中对字节数组和short,int,float,double等的相互转换,提供了一个非常方便的类BitConverter
正如微软官方文档描述的那样:BitConverter Class:Converts base data types to an array of bytes, and an array of bytes to base data types.
也就是说BitConverter类对字节数组和基本的数据类型进行相互转换。
首先,BitCoverter类有一个IsLittleEndian属性,用于判断计算机的体系结构是大端字节序还是小端字节序,大小端这个概念在嵌入式编程和网路编程、串口编程中很常见。另外,C#中直接提供了byte数据类型,类似于C和C++中的unsigned char

数据类型 方法
bool ToBoolean(Byte[], Int32)
char ToChar(Byte[], Int32)
double ToDouble(Byte[], Int32)
short ToInt16(Byte[], Int32)
int ToInt32(Byte[], Int32)
long ToInt64(Byte[], Int32)
float ToSingle(Byte[], Int32)
ushort ToUInt16(Byte[], Int32)
uint ToUInt32(Byte[], Int32)
ulong ToUInt64(Byte[], Int32)

官方提供了一下简单的示例程序,代码如下:

// Example of the BitConverter.IsLittleEndian field.
using System;

class LittleEndDemo
{
    public static void Main( )
    {
        Console.WriteLine( 
            "This example of the BitConverter.IsLittleEndian field " +
            "generates \nthe following output when run on " +
            "x86-class computers.\n");
        Console.WriteLine( "IsLittleEndian:  {0}", 
            BitConverter.IsLittleEndian );
    }
}

/*
This example of the BitConverter.IsLittleEndian field generates
the following output when run on x86-class computers.

IsLittleEndian:  True
*/

经过测试,我的Thinkpad电脑是小端字节序
关于在C#中将字节数组转换成int,可以参考How to convert a byte array to an int (C# Programming Guide),对应的测试代码如下:

1. Example1

本示例初始化一个字节数组,如果计算机体系结构是小端字节序(即,首先存储最低有效字节),则反转该数组,然后调用ToInt32(Byte [],Int32)方法来转换四个字节。 将该数组转换为一个int。 ToInt32(Byte [],Int32)的第二个参数指定字节数组的起始索引。
注意:输出结果会根据你的计算机的体系而不同。

byte[] bytes = { 0, 0, 0, 25 };

// If the system architecture is little-endian (that is, little end first),
// reverse the byte array.
if (BitConverter.IsLittleEndian)
    Array.Reverse(bytes);

int i = BitConverter.ToInt32(bytes, 0);
Console.WriteLine("int: {0}", i);
// Output: int: 25

2.Example2

本则示例,使用BitConvert类的GetBytes(int32)方法将int转换成字节数组
注意:结果会根据你的计算机的体系的大小端而不同。
代码如下:

byte[] bytes = BitConverter.GetBytes(201805978);
Console.WriteLine("byte array: " + BitConverter.ToString(bytes));
// Output: byte array: 9A-50-07-0C

完整的C#代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ByteArrayConvertConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Example1: 将字节数组转换成int
            /*
             This example initializes an array of bytes, reverses the array if the computer architecture is little-endian (that is, the least significant byte is stored first), and then calls the ToInt32(Byte[], Int32) method to convert four bytes in the array to an int. The second argument to ToInt32(Byte[], Int32) specifies the start index of the array of bytes.
             */
            // 字节数组转换成int
            byte[] bytes = { 0, 0, 0, 25 };
            // If the system architecture is little-endian (that is, little end first), reverse the byte array.
            // 如果系统体系结构为小端(即小端优先),则反转字节数组。
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }

            int i = BitConverter.ToInt32(bytes, 0);
            Console.WriteLine("int: {0}", i);
            // OutPut: int: 25

            // Example2: 将int转换成字节数组
            byte[] bytes2 = BitConverter.GetBytes(201805978);
            Console.WriteLine("bytes array:" + BitConverter.ToString(bytes2));
            // OutPut: byte array: 9A-50-07-0C

            // Example3: 将float转换成字节数组
            float f1 = 3.14f;
            byte[] bytes3 = BitConverter.GetBytes(f1);
            Console.WriteLine("float {0} convert to bytes array,result is:" + BitConverter.ToString(bytes3));
            // OutPut: byte array: C3-F5-48-40

            // Example4: 将4个字节的字节数组转换成float
            byte[] bytes4 = { 0xC3, 0xF5, 0x48, 0x40 };
            // 如果系统体系结构为小端(即小端优先),则反转字节数组。
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes4);
            }
            float f2_result = BitConverter.ToSingle(bytes4, 0);
            Console.WriteLine("4 bytes array convert to float, f2_result: {0}", f2_result);
            // OutPut: 4 bytes array convert to float, f2_result: -490.5645


            byte[] bytes5 = { 0x40, 0x48, 0xF5, 0xC3 };
            // 如果系统体系结构为小端(即小端优先),则反转字节数组。
            if (BitConverter.IsLittleEndian)
            {
                // 经过测试,本台机器为小端结构
                // 对字节数组bytes5进行反转后,变为{0xC3, 0xF5, 0x48, 0x40}
                Array.Reverse(bytes5);
            }
            float f2_result2 = BitConverter.ToSingle(bytes5, 0);
            Console.WriteLine("4 bytes array convert to float, f2_result2: {0}", f2_result2);
            // OutPut: 4 bytes array convert to float, f2_result2: 3.14
        }
    }
}

参考资料

发布了131 篇原创文章 · 获赞 38 · 访问量 99万+

猜你喜欢

转载自blog.csdn.net/ccf19881030/article/details/104911459