C#实现十六进制与字符串转换的技术解析

 更新时间:2026年05月22日 08:58:39   作者:加号3  
在嵌入式开发、网络通信、数据解析等场景中,十六进制与字符串的相互转换是 C# 开发者最频繁遇到的基础操作之一,下面我们就来看看具体的实现方法吧

在嵌入式开发、网络通信、数据解析等场景中,十六进制与字符串的相互转换是 C# 开发者最频繁遇到的基础操作之一。这一看似简单的任务,实则涉及编码理论、内存布局、性能优化等多个深层技术维度。

一、问题本质:什么是"十六进制转字符串"

1. 两种截然不同的转换方向

方向 A:字节数组 → 十六进制文本

将二进制数据(如 0x48 0x65 0x6C 0x6C 0x6F)转换为可读的十六进制字符串(如 “48656C6C6F”)。这是日志输出、协议调试、数据校验时的核心需求。

方向 B:十六进制文本 → 字节数组

将字符串形式的数据(如 “FF03A7”)还原为原始字节。这是解析设备上报的 hex 报文、处理配置文件的常见场景。

2. 与"字符串编码"的根本区别

初学者常混淆十六进制转换与字符编码转换:

  • 字符编码(如 UTF-8、GB2312):解决"字符如何映射为字节"的问题。字母 A 在 ASCII 中是 0x41,在 UTF-8 中也是 0x41,但中文"中"在 UTF-8 中是三个字节 0xE4 0xB8 0xAD,在 GB2312 中是两个字节 0xD6 0xD0。
  • 十六进制表示:仅是将字节值的二进制形式以十六进制基数可视化,不涉及字符语义。0x41 的十六进制文本就是 “41”,它不代表字母 A,仅代表数值 65。

理解这一区别至关重要:十六进制字符串是数据的视图,而非数据的语义。

二、字节数组转十六进制字符串

1. 格式化风格选择

工业界存在多种输出风格,需根据场景选择:

2. 大小写敏感性

十六进制字母 A-F 的大小写在数值上等价,但在某些场景有约束:

  • 密码学领域:哈希值、证书序列号通常要求小写,以保证一致性
  • 通信协议:Modbus、CAN 等工业协议文档多用大写
  • URL 编码:百分号编码(如 %3A)要求大写

3. 前导零处理

单字节值小于 16 时(如 0x05),必须输出两位 “05” 而非一位 “5”。遗漏前导零会导致解析歧义——“5A” 是一个字节还是两个字节 0x05 和 0x0A?

三、十六进制字符串转字节数组

1. 输入合法性校验

真实世界的输入从不干净,健壮的转换必须处理:

  • 奇数长度:“ABC” 无法按每两个字符一个字节切分。策略:前置补零(视为 0x0ABC)或抛出异常。
  • 非法字符:包含 G、@、空格、换行等非十六进制字符。策略:严格校验后报错,或过滤忽略(日志场景常用)。
  • 大小写混合:“aB3F” 应被正确解析,大小写不敏感是基本要求。
  • 前缀污染:输入可能带 0x、#、% 等前缀。需先清洗或按协议约定解析。

2. 切分策略

按每两个字符一组切分是最直觉的方式,但需注意:

  • 无分隔符的连续字符串(“AABBCC”)直接双字符切分
  • 带分隔符的字符串(“AA:BB:CC”)需先按分隔符拆分
  • 混合风格(“0xAA, 0xBB”)需设计更复杂的词法分析

四、代码实现

public static string ConvertBytesToString(byte[] bytes, int datalen)
{
  string msg = "";
  for (int i = 0; i < datalen; i++)
  {
      int value = Convert.ToInt32(bytes[i]);
      msg += String.Format("{0:X2} ", value);
  }
  return msg;
}

五、编码陷阱与常见误区

误区一:“十六进制字符串"就是"字符串”

开发者常将 “Hello” 直接当作十六进制文本处理,试图将其转换为字节数组。实际上 “Hello” 是字符序列,其十六进制表示应为 “48656C6C6F”(ASCII 编码下)。混淆"字符串内容"与"字符串的十六进制编码"会导致逻辑错误。

误区二:忽略编码层直接转 Hex

需求"将字符串转为十六进制"存在歧义:

  • 路径 A:字符串 → 按 UTF-8 编码为字节 → 字节转 Hex(如 “中” → E4B8AD)
  • 路径 B:字符串本身就是 Hex 文本(如 “E4B8AD”),需解析为字节明确业务语义是避免 Bug 的第一步。

误区三:大端序与小端序

多字节数据(如 ushort、uint)在内存中的字节排列顺序:

  • 小端序(Little-Endian):低位字节在前。Intel x86/x64 架构采用此方式,0x1234 内存中为 34 12。
  • 大端序(Big-Endian):高位字节在前。网络协议、Modbus 等工业协议多采用此方式。

十六进制字符串通常按大端序书写(如 “1234” 表示值 0x1234),但字节数组可能是小端序。转换时必须显式指定字节序,跨平台或网络通信时尤其危险。

误区四:不可见字符的显示

字节值 0x00(空字符)、0x0A(换行)、0x0D(回车)等在文本编辑器中不可见或干扰显示。十六进制转换是诊断此类问题的唯一可靠手段——不要信任文本框的"空白"。

六、跨平台与兼容性

C# 转换结果需与 C++、Python、JavaScript 等交互时,必须对齐约定:

  • Python 的 hex() 输出 ‘0xff’ 带前缀且小写
  • JavaScript 的 ArrayBuffer 转 Hex 通常无分隔符
  • C 语言的 printf(“%02X”) 默认大写

协议文档应明确指定格式规范,避免联调时的隐性 Bug。

七、调试与诊断技巧

1. 数据比对

当通信双方数据不一致时:

  • 将发送方原始字节与接收方解析字节分别转 Hex 对比
  • 检查是否因编码层(UTF-8 vs ASCII)引入额外字节
  • 验证字节序是否翻转

2. 性能剖析

使用 BenchmarkDotNet 量化不同实现方案:

  • 测量吞吐量(MB/s)
  • 监控 GC 频率与堆分配
  • 对比不同数据长度下的曲线(小数据可能方法调用开销占主导)

3. 边界测试

  • 空数组 / 空字符串
  • 单字节(0x00、0xFF)
  • 极大长度(内存压力测试)
  • 非法字符注入(鲁棒性验证)

八、知识扩展

在 C# 中实现十六进制(Hex)与字符串的相互转换,通常用于数据编码、加密、网络传输等场景。下面介绍几种常用且高效的方法。

1. 字符串 → 十六进制(String to Hex)

将字符串按照指定编码(如 UTF-8、Unicode)转换为对应的十六进制字符串。

方法一:使用 BitConverter + 字节数组

using System.Text;
public static string StringToHex(string input, Encoding encoding = null)
{
    if (string.IsNullOrEmpty(input)) return "";
    encoding ??= Encoding.UTF8;
    byte[] bytes = encoding.GetBytes(input);
    return BitConverter.ToString(bytes).Replace("-", "");
}

特点:简单明了,但 BitConverter.ToString 生成的格式带短横线,需要移除。性能尚可。

方法二:手动逐字节转换(性能最优)

public static string StringToHexFast(string input, Encoding encoding = null)
{
    if (string.IsNullOrEmpty(input)) return "";
    encoding ??= Encoding.UTF8;
    byte[] bytes = encoding.GetBytes(input);
    char[] hex = new char[bytes.Length * 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        byte b = bytes[i];
        hex[i * 2] = GetHexValue(b >> 4);
        hex[i * 2 + 1] = GetHexValue(b & 0x0F);
    }
    return new string(hex);
}
private static char GetHexValue(int val) => (char)(val < 10 ? '0' + val : 'A' + (val - 10));

特点:直接拼接字符数组,无中间字符串开销,适合高频调用。

方法三:使用 Convert.ToHexString(.NET 5+)

public static string StringToHexDotNet(string input, Encoding encoding = null)
{
    if (string.IsNullOrEmpty(input)) return "";
    encoding ??= Encoding.UTF8;
    byte[] bytes = encoding.GetBytes(input);
    return Convert.ToHexString(bytes);
}

特点:.NET 5 及以上内置方法,简洁高效,推荐使用。

2. 十六进制 → 字符串(Hex to String)

将十六进制字符串还原为原始字符串,需要注意编码一致。

方法一:使用 Convert.FromHexString(.NET 5+)

public static string HexToString(string hex, Encoding encoding = null)
{
    if (string.IsNullOrEmpty(hex)) return "";
    encoding ??= Encoding.UTF8;
    byte[] bytes = Convert.FromHexString(hex);
    return encoding.GetString(bytes);
}

特点:最简洁,内置处理大小写和空白(会抛出异常)。

方法二:手动解析(兼容旧版本)

public static string HexToStringManual(string hex, Encoding encoding = null)
{
    if (string.IsNullOrEmpty(hex)) return "";
    encoding ??= Encoding.UTF8;
    // 移除可能存在的短横线或空格
    hex = hex.Replace("-", "").Replace(" ", "");
    if (hex.Length % 2 != 0)
        throw new ArgumentException("无效的十六进制字符串,长度必须是偶数。");
    byte[] bytes = new byte[hex.Length / 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
    }
    return encoding.GetString(bytes);
}

特点:兼容 .NET Framework,可自定义处理分隔符。

3. 完整示例:可配置编码和大小写

using System;
using System.Text;
public static class HexConverter
{
    // 字符串转十六进制(.NET 5+)
    public static string ToHex(string input, Encoding encoding = null, bool upperCase = true)
    {
        if (string.IsNullOrEmpty(input)) return "";
        encoding ??= Encoding.UTF8;
        byte[] bytes = encoding.GetBytes(input);
#if NET5_0_OR_GREATER
        string hex = Convert.ToHexString(bytes);
        return upperCase ? hex : hex.ToLower();
#else
        StringBuilder sb = new StringBuilder(bytes.Length * 2);
        foreach (byte b in bytes)
            sb.Append(b.ToString(upperCase ? "X2" : "x2"));
        return sb.ToString();
#endif
    }
    // 十六进制转字符串
    public static string FromHex(string hex, Encoding encoding = null)
    {
        if (string.IsNullOrEmpty(hex)) return "";
        encoding ??= Encoding.UTF8;
        hex = hex.Replace("-", "").Replace(" ", "");
#if NET5_0_OR_GREATER
        byte[] bytes = Convert.FromHexString(hex);
        return encoding.GetString(bytes);
#else
        if (hex.Length % 2 != 0)
            throw new ArgumentException("十六进制字符串长度必须为偶数。");
        byte[] bytes = new byte[hex.Length / 2];
        for (int i = 0; i < bytes.Length; i++)
            bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
        return encoding.GetString(bytes);
#endif
    }
}

使用示例

string original = "Hello 世界";
string hex = HexConverter.ToHex(original, Encoding.UTF8, false);
Console.WriteLine(hex); // 输出: 48656c6c6f20e4b896e7958c
string restored = HexConverter.FromHex(hex, Encoding.UTF8);
Console.WriteLine(restored); // 输出: Hello 世界

九、结语

十六进制与字符串的转换是软件开发中最基础的操作之一,却也是最容易因"看似简单"而被低估的环节。从编码理论的澄清,到内存布局的理解,再到高性能实现的权衡,每一个环节都影响着程序的健壮性与效率。在 C# 生态中,现代语言特性(Span、SIMD)为这一古老问题提供了新的解法,但技术的选择始终应服务于业务场景——对于配置文件的偶尔解析,简洁可读优先;对于每秒处理百万条报文的网关,零分配与向量化则是必选项。理解原理,方能游刃有余。

到此这篇关于C#实现十六进制与字符串转换的技术解析的文章就介绍到这了,更多相关C#十六进制转字符串内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论