C# BitConverte扩展方法,提供基于大端模式下的数值和字节数组的相互转换
数值转字节数组,以及字节数组转数值,需要注意的是C#的本地字节序是小端模式的,而网络字节序却是(大端模式),所以我用到了IPAddress来进行Host和Network的转换
为了简单以及更好的性能,做了一些取巧的处理方式,尽量避免数组拷贝和反转,看上去很诡异,经过简单测试貌似没啥问题,不敢保证百分百没问题啊,如果有问题,望指出啊,不建议直接拿来用啊
2022-11-03改进net6版
最近开了个net6项目,用到了一些新特性,特意改进了下这个扩展方法库
/// <summary> /// 对BitConverter进行方法扩展 /// </summary> public static class BitConverterExtend { #region 将数值的字节数组写入到span中 public static bool TryWriteInt16(this short num, Span<byte> span, bool bigEndian = true) { if (bigEndian) { num = IPAddress.HostToNetworkOrder(num); } return BitConverter.TryWriteBytes(span, num); } public static bool TryWriteUInt16(this ushort num, Span<byte> span, bool bigEndian = true) { return ((short)num).TryWriteInt16(span, bigEndian); //这样比反转数组快 } public static bool TryWriteInt32(this int num, Span<byte> span, bool bigEndian = true) { if (bigEndian) { num = IPAddress.HostToNetworkOrder(num); } return BitConverter.TryWriteBytes(span, num); } public static bool TryWriteUInt32(this uint num, Span<byte> span, bool bigEndian = true) { return ((int)num).TryWriteInt32(span, bigEndian); } public static bool TryWriteInt64(this long num, Span<byte> span, bool bigEndian = true) { if (bigEndian) { num = IPAddress.HostToNetworkOrder(num); } return BitConverter.TryWriteBytes(span, num); } public static bool TryWriteUInt64(this ulong num, Span<byte> span, bool bigEndian = true) { return ((long)num).TryWriteInt64(span, bigEndian); } public static bool TryWriteFloat(this float num, Span<byte> span, bool bigEndian = true) { bool b = BitConverter.TryWriteBytes(span, num); if (bigEndian) { span[..4].Reverse(); //只能手动反转,注意要先切片后才能反转,因为外部传入的span可能很长 }; return b; } public static bool TryWriteDouble(this double num, Span<byte> span, bool bigEndian = true) { bool b = BitConverter.TryWriteBytes(span, num); if (bigEndian) { span[..8].Reverse(); }; return b; } public static bool WriteString(this string value, Span<byte> span, bool bigEndian = true) { int length = Encoding.UTF8.GetBytes(value.AsSpan(), span[4..]); //先偏移4个字节写数据 return length.TryWriteInt32(span, bigEndian); //再写前4个字节的长度 } #endregion #region 将span转成具体的数据 public static short ReadInt16(this byte[] buffer, int startIndex, bool bigEndian = true) { short num = BitConverter.ToInt16(buffer, startIndex); if (bigEndian) { num = IPAddress.NetworkToHostOrder(num); } return num; } public static ushort ReadUInt16(this byte[] buffer, int startIndex, bool bigEndian = true) { return (ushort)ReadInt16(buffer, startIndex, bigEndian); //因为ushort没有IPAddress.NetworkToHostOrder,所以直接调用ToInt16转换 } public static int ReadInt32(this byte[] buffer, int startIndex, bool bigEndian = true) { int num = BitConverter.ToInt32(buffer, startIndex); if (bigEndian) { num = IPAddress.NetworkToHostOrder(num); } return num; } public static uint ReadUInt32(this byte[] buffer, int startIndex, bool bigEndian = true) { return (uint)ReadInt32(buffer, startIndex, bigEndian); } public static long ReadInt64(this byte[] buffer, int startIndex, bool bigEndian = true) { long num = BitConverter.ToInt64(buffer, startIndex); if (bigEndian) { num = IPAddress.NetworkToHostOrder(num); } return num; } public static ulong ReadUInt64(this byte[] buffer, int startIndex, bool bigEndian = true) { return (ulong)ReadInt64(buffer, startIndex, bigEndian); } public static float ReadFloat(this byte[] buffer, int startIndex, bool bigEndian = true) { float val; if (bigEndian) { Array.Reverse(buffer, startIndex, 4); //需要先反转顺序,因为本地转换函数是小端的 val = BitConverter.ToSingle(buffer, startIndex); Array.Reverse(buffer, startIndex, 4); //用完,再反转回来,避免破坏数据 } else { val = BitConverter.ToSingle(buffer, startIndex); } return val; } public static double ReadDouble(this byte[] buffer, int startIndex, bool bigEndian = true) { double val; if (bigEndian) { Array.Reverse(buffer, startIndex, 8); val = BitConverter.ToDouble(buffer, startIndex); Array.Reverse(buffer, startIndex, 8); } else { val = BitConverter.ToDouble(buffer, startIndex); } return val; } public static string ReadString(this byte[] buffer, int startIndex, bool bigEndian = true) { int length = buffer.ReadInt32(startIndex, bigEndian); return Encoding.UTF8.GetString(buffer, startIndex + 4, length); } #endregion }
测试代码
internal class TestBitConverterExtend { public static void Test() { Console.WriteLine("大端模式#################"); TestAAA(true); Console.WriteLine("小端模式#################"); TestAAA(false); } public static void TestAAA(bool bigEndian) { short p1 = 123; ushort p2 = 456; int p3 = 789; uint p4 = 101112; long p5 = 131415; ulong p6 = 161718; float p7 = 3.14f; double p8 = 2.718281828459045; string p9 = "测试啊123"; byte[] buffer = new byte[1024]; int offset = 0; p1.TryWriteInt16(buffer.AsSpan(offset), bigEndian); offset += 2; p2.TryWriteUInt16(buffer.AsSpan(offset), bigEndian); offset += 2; p3.TryWriteInt32(buffer.AsSpan(offset), bigEndian); offset += 4; p4.TryWriteUInt32(buffer.AsSpan(offset), bigEndian); offset += 4; p5.TryWriteInt64(buffer.AsSpan(offset), bigEndian); offset += 8; p6.TryWriteUInt64(buffer.AsSpan(offset), bigEndian); offset += 8; p7.TryWriteFloat(buffer.AsSpan(offset), bigEndian); offset += 4; p8.TryWriteDouble(buffer.AsSpan(offset), bigEndian); offset += 8; p9.WriteString(buffer.AsSpan(offset), bigEndian); offset = 0; Compare(p1, buffer.ReadInt16(offset, bigEndian), (t1, t2) => t1 == t2); offset += 2; Compare(p2, buffer.ReadUInt16(offset, bigEndian), (t1, t2) => t1 == t2); offset += 2; Compare(p3, buffer.ReadInt32(offset, bigEndian), (t1, t2) => t1 == t2); offset += 4; Compare(p4, buffer.ReadUInt32(offset, bigEndian), (t1, t2) => t1 == t2); offset += 4; Compare(p5, buffer.ReadInt64(offset, bigEndian), (t1, t2) => t1 == t2); offset += 8; Compare(p6, buffer.ReadUInt64(offset, bigEndian), (t1, t2) => t1 == t2); offset += 8; Compare(p7, buffer.ReadFloat(offset, bigEndian), (t1, t2) => t1 == t2); offset += 4; Compare(p8, buffer.ReadDouble(offset, bigEndian), (t1, t2) => t1 == t2); offset += 8; Compare(p9, buffer.ReadString(offset, bigEndian), (t1, t2) => t1 == t2); } static void Compare<T>(T t1, T t2, Func<T, T, bool> func) { Console.WriteLine($"相等:{func(t1, t2)} 原参数:{t1} 写入后再读取的参数:{t2} "); } }
之前的net4.8版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | /// <summary> /// 对BitConverter进行方法扩展 /// </summary> public static class BitConverterExtend { /// <summary> /// 得到数值的网络字节序(大端模式)的字节数组 /// </summary> public static byte [] GetBytes_Network( this short num) { return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(num)); } public static byte [] GetBytes_Network( this ushort num) { return (( short )num).GetBytes_Network(); //这样比反转数组快 } public static byte [] GetBytes_Network( this int num) { return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(num)); } public static byte [] GetBytes_Network( this uint num) { return (( int )num).GetBytes_Network(); } public static byte [] GetBytes_Network( this long num) { return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(num)); } public static byte [] GetBytes_Network( this ulong num) { return (( long )num).GetBytes_Network(); } public static byte [] GetBytes_Network( this float num) { return BitConverter.GetBytes(num).Reverse().ToArray(); } public static byte [] GetBytes_Network( this double num) { return BitConverter.GetBytes(num).Reverse().ToArray(); } public static byte [] GetBytes_UTF8( this string value) { return Encoding.UTF8.GetBytes(value); } /// <summary> /// 将网络字节序(大端模式)的字节数组转成本地字节序(小端模式)的数值 /// </summary> public static short ToInt16_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(networkBytes, startIndex)); //传入引用和起始索引,减少一个数组拷贝 } public static ushort ToUInt16_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return ( ushort )((networkBytes[startIndex++] << 8) | networkBytes[startIndex]); //直接自己按位操作,这样最快 } public static int ToInt32_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(networkBytes, startIndex)); } public static uint ToUInt32_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return ( uint )((networkBytes[startIndex++] << 24) | (networkBytes[startIndex++] << 16) | (networkBytes[startIndex++] << 8) | networkBytes[startIndex]); } public static long ToInt64_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return IPAddress.NetworkToHostOrder(BitConverter.ToInt64(networkBytes, startIndex)); } public static ulong ToUInt64_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return ( ulong )((networkBytes[startIndex++] << 56) | (networkBytes[startIndex++] << 48) | (networkBytes[startIndex++] << 40) | (networkBytes[startIndex++] << 32) | (networkBytes[startIndex++] << 24) | (networkBytes[startIndex++] << 16) | (networkBytes[startIndex++] << 8) | networkBytes[startIndex]); } public static float ToFloat_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return BitConverter.ToSingle(CopyAndReverse(networkBytes, startIndex, 4), 0); } /// <summary> /// 拷贝反转 /// </summary> static byte [] CopyAndReverse( byte [] networkBytes, int startIndex, int len) { byte [] bs = new byte [len]; for ( int i = 0; i < len; i++) { bs[len - 1 - i] = networkBytes[startIndex + i]; //反转拷贝 } return bs; } public static double ToDouble_ByNetworkBytes( this byte [] networkBytes, int startIndex) { return BitConverter.ToDouble(CopyAndReverse(networkBytes, startIndex, 8), 0); } public static string GetString_UTF8( this byte [] value, int startIndex, int count) { return Encoding.UTF8.GetString(value, startIndex, count); } } |
标签:
C#学习笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2020-06-28 C# 生成一个当前程序唯一的短字符串