C#實(shí)現(xiàn)十六進(jìn)制與字符串轉(zhuǎn)換的技術(shù)解析
在嵌入式開(kāi)發(fā)、網(wǎng)絡(luò)通信、數(shù)據(jù)解析等場(chǎng)景中,十六進(jìn)制與字符串的相互轉(zhuǎn)換是 C# 開(kāi)發(fā)者最頻繁遇到的基礎(chǔ)操作之一。這一看似簡(jiǎn)單的任務(wù),實(shí)則涉及編碼理論、內(nèi)存布局、性能優(yōu)化等多個(gè)深層技術(shù)維度。
一、問(wèn)題本質(zhì):什么是"十六進(jìn)制轉(zhuǎn)字符串"
1. 兩種截然不同的轉(zhuǎn)換方向
方向 A:字節(jié)數(shù)組 → 十六進(jìn)制文本
將二進(jìn)制數(shù)據(jù)(如 0x48 0x65 0x6C 0x6C 0x6F)轉(zhuǎn)換為可讀的十六進(jìn)制字符串(如 “48656C6C6F”)。這是日志輸出、協(xié)議調(diào)試、數(shù)據(jù)校驗(yàn)時(shí)的核心需求。
方向 B:十六進(jìn)制文本 → 字節(jié)數(shù)組
將字符串形式的數(shù)據(jù)(如 “FF03A7”)還原為原始字節(jié)。這是解析設(shè)備上報(bào)的 hex 報(bào)文、處理配置文件的常見(jiàn)場(chǎng)景。
2. 與"字符串編碼"的根本區(qū)別
初學(xué)者?;煜M(jìn)制轉(zhuǎn)換與字符編碼轉(zhuǎn)換:
- 字符編碼(如 UTF-8、GB2312):解決"字符如何映射為字節(jié)"的問(wèn)題。字母 A 在 ASCII 中是 0x41,在 UTF-8 中也是 0x41,但中文"中"在 UTF-8 中是三個(gè)字節(jié) 0xE4 0xB8 0xAD,在 GB2312 中是兩個(gè)字節(jié) 0xD6 0xD0。
- 十六進(jìn)制表示:僅是將字節(jié)值的二進(jìn)制形式以十六進(jìn)制基數(shù)可視化,不涉及字符語(yǔ)義。0x41 的十六進(jìn)制文本就是 “41”,它不代表字母 A,僅代表數(shù)值 65。
理解這一區(qū)別至關(guān)重要:十六進(jìn)制字符串是數(shù)據(jù)的視圖,而非數(shù)據(jù)的語(yǔ)義。
二、字節(jié)數(shù)組轉(zhuǎn)十六進(jìn)制字符串
1. 格式化風(fēng)格選擇
工業(yè)界存在多種輸出風(fēng)格,需根據(jù)場(chǎng)景選擇:

2. 大小寫(xiě)敏感性
十六進(jìn)制字母 A-F 的大小寫(xiě)在數(shù)值上等價(jià),但在某些場(chǎng)景有約束:
- 密碼學(xué)領(lǐng)域:哈希值、證書(shū)序列號(hào)通常要求小寫(xiě),以保證一致性
- 通信協(xié)議:Modbus、CAN 等工業(yè)協(xié)議文檔多用大寫(xiě)
- URL 編碼:百分號(hào)編碼(如 %3A)要求大寫(xiě)
3. 前導(dǎo)零處理
單字節(jié)值小于 16 時(shí)(如 0x05),必須輸出兩位 “05” 而非一位 “5”。遺漏前導(dǎo)零會(huì)導(dǎo)致解析歧義——“5A” 是一個(gè)字節(jié)還是兩個(gè)字節(jié) 0x05 和 0x0A?
三、十六進(jìn)制字符串轉(zhuǎn)字節(jié)數(shù)組
1. 輸入合法性校驗(yàn)
真實(shí)世界的輸入從不干凈,健壯的轉(zhuǎn)換必須處理:
- 奇數(shù)長(zhǎng)度:“ABC” 無(wú)法按每?jī)蓚€(gè)字符一個(gè)字節(jié)切分。策略:前置補(bǔ)零(視為 0x0ABC)或拋出異常。
- 非法字符:包含 G、@、空格、換行等非十六進(jìn)制字符。策略:嚴(yán)格校驗(yàn)后報(bào)錯(cuò),或過(guò)濾忽略(日志場(chǎng)景常用)。
- 大小寫(xiě)混合:“aB3F” 應(yīng)被正確解析,大小寫(xiě)不敏感是基本要求。
- 前綴污染:輸入可能帶 0x、#、% 等前綴。需先清洗或按協(xié)議約定解析。
2. 切分策略
按每?jī)蓚€(gè)字符一組切分是最直覺(jué)的方式,但需注意:
- 無(wú)分隔符的連續(xù)字符串(“AABBCC”)直接雙字符切分
- 帶分隔符的字符串(“AA:BB:CC”)需先按分隔符拆分
- 混合風(fēng)格(“0xAA, 0xBB”)需設(shè)計(jì)更復(fù)雜的詞法分析
四、代碼實(shí)現(xiàn)
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;
}
五、編碼陷阱與常見(jiàn)誤區(qū)
誤區(qū)一:“十六進(jìn)制字符串"就是"字符串”
開(kāi)發(fā)者常將 “Hello” 直接當(dāng)作十六進(jìn)制文本處理,試圖將其轉(zhuǎn)換為字節(jié)數(shù)組。實(shí)際上 “Hello” 是字符序列,其十六進(jìn)制表示應(yīng)為 “48656C6C6F”(ASCII 編碼下)。混淆"字符串內(nèi)容"與"字符串的十六進(jìn)制編碼"會(huì)導(dǎo)致邏輯錯(cuò)誤。
誤區(qū)二:忽略編碼層直接轉(zhuǎn) Hex
需求"將字符串轉(zhuǎn)為十六進(jìn)制"存在歧義:
- 路徑 A:字符串 → 按 UTF-8 編碼為字節(jié) → 字節(jié)轉(zhuǎn) Hex(如 “中” → E4B8AD)
- 路徑 B:字符串本身就是 Hex 文本(如 “E4B8AD”),需解析為字節(jié)明確業(yè)務(wù)語(yǔ)義是避免 Bug 的第一步。
誤區(qū)三:大端序與小端序
多字節(jié)數(shù)據(jù)(如 ushort、uint)在內(nèi)存中的字節(jié)排列順序:
- 小端序(Little-Endian):低位字節(jié)在前。Intel x86/x64 架構(gòu)采用此方式,0x1234 內(nèi)存中為 34 12。
- 大端序(Big-Endian):高位字節(jié)在前。網(wǎng)絡(luò)協(xié)議、Modbus 等工業(yè)協(xié)議多采用此方式。
十六進(jìn)制字符串通常按大端序書(shū)寫(xiě)(如 “1234” 表示值 0x1234),但字節(jié)數(shù)組可能是小端序。轉(zhuǎn)換時(shí)必須顯式指定字節(jié)序,跨平臺(tái)或網(wǎng)絡(luò)通信時(shí)尤其危險(xiǎn)。
誤區(qū)四:不可見(jiàn)字符的顯示
字節(jié)值 0x00(空字符)、0x0A(換行)、0x0D(回車(chē))等在文本編輯器中不可見(jiàn)或干擾顯示。十六進(jìn)制轉(zhuǎn)換是診斷此類(lèi)問(wèn)題的唯一可靠手段——不要信任文本框的"空白"。
六、跨平臺(tái)與兼容性
C# 轉(zhuǎn)換結(jié)果需與 C++、Python、JavaScript 等交互時(shí),必須對(duì)齊約定:
- Python 的 hex() 輸出 ‘0xff’ 帶前綴且小寫(xiě)
- JavaScript 的 ArrayBuffer 轉(zhuǎn) Hex 通常無(wú)分隔符
- C 語(yǔ)言的 printf(“%02X”) 默認(rèn)大寫(xiě)
協(xié)議文檔應(yīng)明確指定格式規(guī)范,避免聯(lián)調(diào)時(shí)的隱性 Bug。
七、調(diào)試與診斷技巧
1. 數(shù)據(jù)比對(duì)
當(dāng)通信雙方數(shù)據(jù)不一致時(shí):
- 將發(fā)送方原始字節(jié)與接收方解析字節(jié)分別轉(zhuǎn) Hex 對(duì)比
- 檢查是否因編碼層(UTF-8 vs ASCII)引入額外字節(jié)
- 驗(yàn)證字節(jié)序是否翻轉(zhuǎn)
2. 性能剖析
使用 BenchmarkDotNet 量化不同實(shí)現(xiàn)方案:
- 測(cè)量吞吐量(MB/s)
- 監(jiān)控 GC 頻率與堆分配
- 對(duì)比不同數(shù)據(jù)長(zhǎng)度下的曲線(xiàn)(小數(shù)據(jù)可能方法調(diào)用開(kāi)銷(xiāo)占主導(dǎo))
3. 邊界測(cè)試
- 空數(shù)組 / 空字符串
- 單字節(jié)(0x00、0xFF)
- 極大長(zhǎng)度(內(nèi)存壓力測(cè)試)
- 非法字符注入(魯棒性驗(yàn)證)
八、知識(shí)擴(kuò)展
在 C# 中實(shí)現(xiàn)十六進(jìn)制(Hex)與字符串的相互轉(zhuǎn)換,通常用于數(shù)據(jù)編碼、加密、網(wǎng)絡(luò)傳輸?shù)葓?chǎng)景。下面介紹幾種常用且高效的方法。
1. 字符串 → 十六進(jìn)制(String to Hex)
將字符串按照指定編碼(如 UTF-8、Unicode)轉(zhuǎn)換為對(duì)應(yīng)的十六進(jìn)制字符串。
方法一:使用 BitConverter + 字節(jié)數(shù)組
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("-", "");
}特點(diǎn):簡(jiǎn)單明了,但 BitConverter.ToString 生成的格式帶短橫線(xiàn),需要移除。性能尚可。
方法二:手動(dòng)逐字節(jié)轉(zhuǎn)換(性能最優(yōu))
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));特點(diǎn):直接拼接字符數(shù)組,無(wú)中間字符串開(kāi)銷(xiāo),適合高頻調(diào)用。
方法三:使用 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);
}特點(diǎn):.NET 5 及以上內(nèi)置方法,簡(jiǎn)潔高效,推薦使用。
2. 十六進(jìn)制 → 字符串(Hex to String)
將十六進(jìn)制字符串還原為原始字符串,需要注意編碼一致。
方法一:使用 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);
}特點(diǎn):最簡(jiǎn)潔,內(nèi)置處理大小寫(xiě)和空白(會(huì)拋出異常)。
方法二:手動(dòng)解析(兼容舊版本)
public static string HexToStringManual(string hex, Encoding encoding = null)
{
if (string.IsNullOrEmpty(hex)) return "";
encoding ??= Encoding.UTF8;
// 移除可能存在的短橫線(xiàn)或空格
hex = hex.Replace("-", "").Replace(" ", "");
if (hex.Length % 2 != 0)
throw new ArgumentException("無(wú)效的十六進(jìn)制字符串,長(zhǎng)度必須是偶數(shù)。");
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);
}特點(diǎn):兼容 .NET Framework,可自定義處理分隔符。
3. 完整示例:可配置編碼和大小寫(xiě)
using System;
using System.Text;
public static class HexConverter
{
// 字符串轉(zhuǎn)十六進(jìn)制(.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
}
// 十六進(jìn)制轉(zhuǎn)字符串
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("十六進(jìn)制字符串長(zhǎng)度必須為偶數(shù)。");
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 世界
九、結(jié)語(yǔ)
十六進(jìn)制與字符串的轉(zhuǎn)換是軟件開(kāi)發(fā)中最基礎(chǔ)的操作之一,卻也是最容易因"看似簡(jiǎn)單"而被低估的環(huán)節(jié)。從編碼理論的澄清,到內(nèi)存布局的理解,再到高性能實(shí)現(xiàn)的權(quán)衡,每一個(gè)環(huán)節(jié)都影響著程序的健壯性與效率。在 C# 生態(tài)中,現(xiàn)代語(yǔ)言特性(Span、SIMD)為這一古老問(wèn)題提供了新的解法,但技術(shù)的選擇始終應(yīng)服務(wù)于業(yè)務(wù)場(chǎng)景——對(duì)于配置文件的偶爾解析,簡(jiǎn)潔可讀優(yōu)先;對(duì)于每秒處理百萬(wàn)條報(bào)文的網(wǎng)關(guān),零分配與向量化則是必選項(xiàng)。理解原理,方能游刃有余。
到此這篇關(guān)于C#實(shí)現(xiàn)十六進(jìn)制與字符串轉(zhuǎn)換的技術(shù)解析的文章就介紹到這了,更多相關(guān)C#十六進(jìn)制轉(zhuǎn)字符串內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- c# 實(shí)現(xiàn)獲取漢字十六進(jìn)制Unicode編碼字符串的實(shí)例
- C#十六進(jìn)制字符串轉(zhuǎn)十進(jìn)制int的方法
- C#中如何轉(zhuǎn)換十進(jìn)制、十六進(jìn)制、八進(jìn)制和二進(jìn)制基數(shù)
- C#實(shí)現(xiàn)十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制的幾種方式
- C#實(shí)現(xiàn)十六進(jìn)制與十進(jìn)制相互轉(zhuǎn)換以及及不同進(jìn)制表示
- C#操作Byte數(shù)組和十六進(jìn)制進(jìn)行互轉(zhuǎn)
- C# 進(jìn)制轉(zhuǎn)換的實(shí)現(xiàn)(二進(jìn)制、十六進(jìn)制、十進(jìn)制互轉(zhuǎn))
相關(guān)文章
C#操作SQLite數(shù)據(jù)庫(kù)之讀寫(xiě)數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了C#操作SQLite數(shù)據(jù)庫(kù)之讀寫(xiě)數(shù)據(jù)庫(kù)的方法,簡(jiǎn)單分析了C#針對(duì)SQLite數(shù)據(jù)庫(kù)的讀寫(xiě)及顯示等操作相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
WPF實(shí)現(xiàn)背景燈光隨鼠標(biāo)閃動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了WPF實(shí)現(xiàn)背景燈光隨鼠標(biāo)閃動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08
C#中Write()和WriteLine()的區(qū)別分析
這篇文章主要介紹了C#中Write()和WriteLine()的區(qū)別分析,需要的朋友可以參考下2020-11-11
C#運(yùn)算符之與,或,異或及移位運(yùn)算小結(jié)
本文是對(duì)C#中的與,或,異或及移位運(yùn)算進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-10-10
C#將Sql數(shù)據(jù)保存到Excel文件中的方法
這篇文章主要介紹了C#將Sql數(shù)據(jù)保存到Excel文件中的方法,文中的ExportExcel可起到將sql數(shù)據(jù)導(dǎo)出為Excel的作用,需要的朋友可以參考下2014-08-08

