天天看點

位元組數組和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>
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#中位元組數組和基本資料類型的互相轉換

在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
        }
    }
}           

複制

參考資料

  • How to convert a byte array into double in C?
  • Fastest way to convert 4 bytes to float in c++
  • How to convert a byte array to an int (C# Programming Guide)
  • BitConverter.IsLittleEndian Field- C#
  • BitConverter Class-C#