在进行Modbus协议通信和网络编程时,有时需要将从串口或者网络中接收的数据从字节数组转换成对应的int,float,double等数据,有时还要考虑大小端字节序以及Swap的问题,发现在C++中需要自己写相关的转换函数,于是/写了一个函数,用于从输入的byte数组中获取指定类型的数据,目前支持int16,int32,int64,float,double,对应的代码如下:
#ifndef _BYTECONVERTTOOLS_H
#define _BYTECONVERTTOOLS_H
#include <algorithm>
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) {
// 小端字节序
std::copy(buffer + startPos, buffer + endPos, std::back_inserter(tmp8));
}
else {
// 大端字节序,则将字节数组进行反转
std::reverse_copy(buffer + startPos, buffer + endPos, std::back_inserter(tmp8));
}
// 将8位的数组tmp8转换成16位的数组tmp16
memcpy(tmp16.data(), tmp8.data(), byteNum);
if (isSwapByte)
{
// 将tmp16反转
std::reverse(tmp16.begin(), tmp16.end());
Uint8Array tmp1(byteNum);
// 将16位的tmp16转换成8位的tmp1
memcpy(tmp1.data(), tmp16.data(), byteNum);
// 将tmp1进行反转
std::reverse(tmp1.begin(), tmp1.end());
// 将tmp1的内容拷贝到tmp16中
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);
}
};
#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#中对字节数组和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),对应的测试代码如下:
本示例初始化一个字节数组,如果计算机体系结构是小端字节序(即,首先存储最低有效字节),则反转该数组,然后调用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
本则示例,使用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
}
}
}