C# 对象、文件与二进制串(byte数组)之间的转换

本文介绍在C#中如何实现object与byte数组之间的高效转换,包括使用BinaryFormatter序列化与反序列化的方法,以及利用Marshal类进行结构体转换的技术细节。

1.关于本文

在使用C#下的TCP(类TcpClient)、UDP(类UdpClient)协议传输信息时,都需要将信息转换为byte类型的数组进行发送。本文实现了两种object与byte数组的转换和一种文件与byte数组转换的方式。基础类型的数据,可以用BitConverter类中的函数进行转换。

2.object与byte[]的相互转换:使用IFormatter的Serialize和Deserialize进行序列化与反序列化

实现这个功能,需要先引用三个命名空间:System.IO、System.Runtime.Serialization、System.Runtime.Serialization.Formatters.Binary;

/// <summary>
/// 工具类:对象与二进制流间的转换
/// </summary>
class ByteConvertHelper
{
    /// <summary>
    /// 将对象转换为byte数组
    /// </summary>
    /// <param name="obj">被转换对象</param>
    /// <returns>转换后byte数组</returns>
    public static byte[] Object2Bytes(object obj)
    {
        byte[] buff;
        using (MemoryStream ms = new MemoryStream())
        {
            IFormatter iFormatter = new BinaryFormatter();
            iFormatter.Serialize(ms, obj);
            buff = ms.GetBuffer();
        }
        return buff;
    }

    /// <summary>
    /// 将byte数组转换成对象
    /// </summary>
    /// <param name="buff">被转换byte数组</param>
    /// <returns>转换完成后的对象</returns>
    public static object Bytes2Object(byte[] buff)
    {
        object obj;
        using (MemoryStream ms = new MemoryStream(buff))
        {
            IFormatter iFormatter = new BinaryFormatter();
            obj = iFormatter.Deserialize(ms);
        }
        return obj;
    }
}

调用示例:

假设有一个添加了Serializable特性的结构:

/// <summary>
/// 测试结构
/// </summary>
[Serializable]
struct TestStructure
{
    public string A; //变量A
    public char B;   //变量B
    public int C;    //变量C

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="paraA"></param>
    /// <param name="paraB"></param>
    /// <param name="paraC"></param>
    public TestStructure(string paraA, char paraB, int paraC)
    {
        this.A = paraA;
        this.B = paraB;
        this.C = paraC;
    }

    /// <summary>
    /// 输出本结构中内容
    /// </summary>
    /// <returns></returns>
    public string DisplayInfo()
    {
        return string.Format("A:{0};B:{1};C:{2}", this.A, this.B, this.C);
    }
}

那么调用下面的代码可以完成这个结构的转换

static void Main(string[] args)
{
    TestStructure tsA = new TestStructure("1234", '5', 6);
    byte[] bytTemp = ByteConvertHelper.Object2Bytes(tsA);
    Console.WriteLine("数组长度:" + bytTemp.Length);
    TestStructure tsB = (TestStructure)ByteConvertHelper.Bytes2Object(bytTemp);
    Console.WriteLine(tsB.DisplayInfo());

    Console.ReadLine();
}

输出为:

193913_EniJ_1425762.png

需要注意的是,用这个方式进行结构与byte数组间的转换,结构或类必须有Serializable特性。否则会有异常(SerializationException):“程序集 "XXX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 中的类型 "XXX.XXX" 未标记为可序列化”

另外,这个方式生成的byte数组长度较大

3.使用Marshal类的StructureToPtr与PtrToStructure函数对object与byte数组进行转换

实现这个功能,需要先引用命名空间:System.Runtime.InteropServices

/// <summary>
/// 工具类:对象与二进制流间的转换
/// </summary>
class ByteConvertHelper
{
    /// <summary>
    /// 将对象转换为byte数组
    /// </summary>
    /// <param name="obj">被转换对象</param>
    /// <returns>转换后byte数组</returns>
    public static byte[] Object2Bytes(object obj)
    {
        byte[] buff = new byte[Marshal.SizeOf(obj)];
        IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buff, 0);
        Marshal.StructureToPtr(obj, ptr, true);
        return buff;
    }

    /// <summary>
    /// 将byte数组转换成对象
    /// </summary>
    /// <param name="buff">被转换byte数组</param>
    /// <param name="typ">转换成的类名</param>
    /// <returns>转换完成后的对象</returns>
    public static object Bytes2Object(byte[] buff, Type typ)
    {
        IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buff, 0);
        return Marshal.PtrToStructure(ptr, typ);
    }
}

调用示例:

现有结构如下(就是比上面示例中的结构少了特性Serializable):

/// <summary>
/// 测试结构
/// </summary>
struct TestStructure
{
    public string A; //变量A
    public char B;   //变量B
    public int C;    //变量C

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="paraA"></param>
    /// <param name="paraB"></param>
    /// <param name="paraC"></param>
    public TestStructure(string paraA, char paraB, int paraC)
    {
        this.A = paraA;
        this.B = paraB;
        this.C = paraC;
    }

    /// <summary>
    /// 输出本结构中内容
    /// </summary>
    /// <returns></returns>
    public string DisplayInfo()
    {
        return string.Format("A:{0};B:{1};C:{2}", this.A, this.B, this.C);
    }
}

调用下面的代码可以完成转换:

static void Main(string[] args)
{
    TestStructure tsA = new TestStructure("1234", '5', 6);
    byte[] bytTemp = ByteConvertHelper.Object2Bytes(tsA);
    Console.WriteLine("数组长度:" + bytTemp.Length);
    TestStructure tsB = (TestStructure)ByteConvertHelper.Bytes2Object(
        bytTemp, Type.GetType("ByteConverter2.TestStructure"));
    Console.WriteLine(tsB.DisplayInfo());

    Console.ReadLine();
}

运行示例:

194511_yEp8_1425762.png

可以看到,数组长度仅为12,比上面示例中转换的byte[]数组短了非常多,更加节省空间

4.使用FileStream将文件与byte数组相互转换

实现这个功能,需要先引用命名空间:System.IO

/// <summary>
/// 工具类:文件与二进制流间的转换
/// </summary>
class FileBinaryConvertHelper
{
    /// <summary>
    /// 将文件转换为byte数组
    /// </summary>
    /// <param name="path">文件地址</param>
    /// <returns>转换后的byte数组</returns>
    public static byte[] File2Bytes(string path)
    {
        if(!File.Exists(path))
        {
            return new byte[0];
        }

        FileInfo fi = new FileInfo(path);
        byte[] buff = new byte[fi.Length];

        FileStream fs = fi.OpenRead();
        fs.Read(buff, 0, Convert.ToInt32(fs.Length));
        fs.Close();

        return buff;
    }

    /// <summary>
    /// 将byte数组转换为文件并保存到指定地址
    /// </summary>
    /// <param name="buff">byte数组</param>
    /// <param name="savepath">保存地址</param>
    public static void Bytes2File(byte[] buff, string savepath)
    {
        if (File.Exists(savepath))
        {
            File.Delete(savepath);
        }

        FileStream fs = new FileStream(savepath, FileMode.CreateNew);
        BinaryWriter bw = new BinaryWriter(fs);
        bw.Write(buff, 0, buff.Length);
        bw.Close();
        fs.Close();
    }
}

假设有文件test.txt,调用下面代码可以将test.txt写到byte数组中,并将这个byte数组的内容写入到文件output.txt里

static void Main(string[] args)
{
    byte[] bytTemp = FileBinaryConvertHelper.File2Bytes("test.txt");
    Console.WriteLine("数组长度:" + bytTemp.Length);
    FileBinaryConvertHelper.Bytes2File(bytTemp, "output.txt");
    Console.WriteLine("输出完成");

    Console.ReadLine();
}

运行结果:

195722_x73a_1425762.png

END

转载于:https://my.oschina.net/Tsybius2014/blog/352409

<think>好的,我现在要解决用户的问题:如何在C#中将byte数组从十进制转换为十六进制。用户之前可能看过一些Java的示例,现在需要转换C#,所以我需要先理解两者的区别,并提供正确的C#实现方法。首先,用户提到的引用中有Java代码,比如使用StringBuilder和String.format来格式化每个byte为两位十六进制。在C#中,类似的方法是使用StringBuilder和ToString("X2")这样的格式。但是要注意,Java的byte是有符号的,而C#byte是0-255无符号的,处理方式可能不同,但在转换为十六进制时应该不会有负数的问题,因为每个byte都会被当作0-255处理。接下来,引用3中的Java代码将byte数组转换为十六进制字符串,用到了Integer.toHexString,并且处理了单个字符的情况,补0。在C#中,可以使用ToString("X2")直接实现两位的十六进制,不需要手动补0,因为格式说明符X2会自动处理。然后,用户的需求是转换byte数组中的每个元素的十进制值为十六进制字符串。例如,byte数组中的元素是1,12,转换成十六进制就是"01","0C"。因此,需要遍历数组中的每个byte,将其转换为两位十六进制,然后拼接起来。 需要注意的是,C#中的byte类型是0到255,所以转换为十六进制时,直接使用ToString("X2")即可,确保两位大写。例如,15转换为"0F",而不是"F"。同时,如果用户需要小写,可以使用"x2"格式。另外,引用4中提到C#中的转换可能需要处理大数,但这里只是单个byte,所以不需要考虑这个问题。每个byte单独转换即可。综合这些信息,正确的C#方法应该是遍历byte数组,使用BitConverter.ToString方法或者逐个处理每个byte。BitConverter.ToString会生成用连字符分隔的字符串,例如“01-0C”,但用户可能不需要连字符,所以需要替换掉。或者手动遍历每个byte,使用ToString("X2"),然后拼接成字符串。 例如,使用BitConverter.ToString(arr).Replace("-",""),这样可以直接得到连续的十六进制字符串。或者手动循环,逐个转换,这样更可控。需要测试这两种方法的正确性。例如,byte数组{1,12}应该转换为"010C"。使用BitConverter的话,会得到"01-0C",替换掉连字符后就是"010C"。而手动循环的话,每个byte转换为"01"和"0C",拼接起来也一样。 因此,两种方法都可以,但BitConverter更简洁,而手动循环可能更灵活,比如处理大小写或者特殊格式。另外,用户可能希望得到大写的十六进制,所以需要使用大写格式。例如,ToString("X2")。最后,给出示例代码,并说明两种方法的优缺点。可能用户需要的是一个高效且简洁的方法,所以推荐使用BitConverter,或者手动循环的方法,确保正确性。需要检查是否有特殊情况,例如byte数组为空,或者包含0值的情况,但一般来说,这两种方法都能处理。总结,C#中将byte数组转换为十六进制字符串的方法有两种主要方式:使用BitConverter类或逐个转换每个byte。需要根据用户的具体需求选择合适的方法,并处理格式问题。</think>在C#中将byte数组从十进制转换为十六进制字符串,可通过以下两种常用方法实现: --- ### 方法一:使用`BitConverter`类 ```csharp byte[] data = { 1, 12 }; string hexResult = BitConverter.ToString(data).Replace("-", ""); Console.WriteLine(hexResult); // 输出:010C ``` - **原理**:`BitConverter.ToString(byte[])`会将每个byte转换为两位十六进制字符,并用连字符`-`分隔,通过`Replace("-", "")`移除连字符[^3]。 - **优点**:代码简洁,适合快速实现。 - **注意**:结果默认大写字母,若需小写可追加`.ToLower()`。 --- ### 方法二:手动遍历转换 ```csharp byte[] data = { 1, 12 }; StringBuilder sb = new StringBuilder(); foreach (byte b in data) { sb.Append(b.ToString("X2")); // "X2"表示两位大写十六进制 } string hexResult = sb.ToString(); Console.WriteLine(hexResult); // 输出:010C ``` - **原理**:逐个byte转换为两位十六进制字符,`"X2"`格式确保补零(如`0x0F`显示为`0F`)[^4]。 - **优点**:灵活控制格式(如大小写、分隔符)。 --- ### 关键区别 | 特性 | BitConverter方法 | 手动遍历方法 | |--------------------|-------------------------|--------------------------| | 代码复杂度 | 简单 | 中等 | | 格式控制 | 固定大写,需移除连字符 | 可自定义大小写、格式 | | 性能 | 较高(底层优化) | 较高(直接拼接) | --- ### 相关问题 1. **如何处理大写的十六进制小写的转换?** 答:在`ToString("X2")`中使用`x2`格式符即可生成小写(如`0f`)。 2. **如果byte数组包含负数如何转换?** 答:C#中`byte`类型范围是0-255,不存在负数。若用`sbyte`(-128~127),需先转换为`byte`:`(byte)value`[^2]。 3. **如何将十六进制字符串还原为byte数组?** 答:可通过`Convert.FromHexString(hexString)`(.NET 5+)或逐字符解析实现。 --- ### 参考实现验证 ```csharp // 示例输入:byte[] {1, 12} // 预期输出:"010C" // 方法一验证 string result1 = BitConverter.ToString(new byte[] {1, 12}).Replace("-", ""); // 方法二验证 StringBuilder sb = new StringBuilder(); foreach (byte b in new byte[] {1, 12}) { sb.Append(b.ToString("X2")); } string result2 = sb.ToString(); ``` 两种方法均输出`010C`,验证通过[^3][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值