How do you convert a byte array to a hexadecimal string, and vice versa?
如何将字节数组转换为十六进制字符串,反之亦然?
指: </P >
1 2 3 4 5 6 7 | public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) hex.AppendFormat("{0:x2}", b); return hex.ToString(); } |
或: </P >
1 2 3 4 | public static string ByteArrayToString(byte[] ba) { return BitConverter.ToString(ba).Replace("-",""); } |
这是部甚至更多的variants做它,例如在这里。 </P >
《逆向转换:一起去呢 </P >
1 2 3 4 5 6 7 8 | public static byte[] StringToByteArray(String hex) { int NumberChars = hex.Length; byte[] bytes = new byte[NumberChars / 2]; for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } |
使用
性能分析
注:自2015年8月20日起新领导。好的。
我通过一些原始的
警告:不要将这些统计数据用于任何具体的内容;它们只是示例数据的一次运行。如果您确实需要一流的性能,请在代表您的生产需要的环境中,使用代表您将要使用的数据来测试这些方法。好的。结果
- 按字节
unsafe 查找(通过codesinchaos)(通过airbreather添加到测试repo)- 文本:4727.85(105.2X)
- 句子:0.28(99.7x)
- 按字节查找(通过codesinchaos)
- 文本:10853.96(更快45.8倍)
- 句子:0.65(快42.7倍)
- 字节操作2(通过codesinchaos)
- 文本:12967.69(更快38.4倍)
- 句子:0.73(快37.9倍)
- 字节操作(通过Waleed EISSA)
- 文本:16856.64(快29.5倍)
- 句子:0.70(快39.5倍)
- 查找/移动(通过Nathan Moinvaziri)
- 文本:23201.23(更快21.4倍)
- 句子:1.24(快22.3倍)
- 按一点查找(通过Brian Lambert)
- 文本:23879.41(快20.8倍)
- 句子:1.15(快23.9倍)
BitConverter (经Tomalak)- 文本:113269.34(更快4.4倍)
- 句子:9.98(快2.8倍)
{SoapHexBinary}.ToString (通过mykroft)- 文本:178601.39(更快2.8倍)
- 句子:10.68(快2.6倍)
{byte}.ToString("X2") (使用foreach (来源于威尔·迪恩的回答)- 文本:308805.38(更快2.4倍)
- 句子:16.89(快2.4倍)
{byte}.ToString("X2") (使用{IEnumerable}.Aggregate ,需要system.linq)(通过mark)- 文本:352828.20(更快2.1X)
- 句子:16.87(快2.4倍)
Array.ConvertAll (使用string.Join (via will-dean)- 文本:675451.57(更快1.1X)
- 句子:17.95(快2.2倍)
Array.ConvertAll (使用string.Concat ,要求.NET 4.0)(via will-dean)- 文本:752078.70(快1.0倍)
- 句子:18.28(快2.2倍)
{StringBuilder}.AppendFormat (使用foreach (通过tomalak)- 文本:672115.77(更快1.1倍)
- 句子:36.82(快1.1倍)
{StringBuilder}.AppendFormat (使用{IEnumerable}.Aggregate ,需要system.linq)(来源于tomalak的答案)- 文本:718380.63(快1.0倍)
- 句子:39.71(快1.0倍)
查找表已经领先于字节操作。基本上,有某种形式的预计算,任何给定的半字节或字节都是十六进制的。然后,当您翻阅数据时,您只需查找下一部分,看看它是什么十六进制字符串。然后,该值以某种方式添加到生成的字符串输出中。在很长一段时间内,一些开发人员可能难以读取字节操作,这是最有效的方法。好的。
您最好的选择仍然是找到一些具有代表性的数据,并在类似生产环境中进行尝试。如果您有不同的内存约束,您可能更喜欢分配较少的方法,而不是更快但占用更多内存的方法。好的。测试代码
请随意使用我使用的测试代码。这里包含一个版本,但是可以自由克隆repo并添加您自己的方法。如果您发现有什么有趣的或者想要帮助改进它使用的测试框架,请提交一个请求。好的。
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | static string ByteArrayToHexStringViaStringJoinArrayConvertAll(byte[] bytes) { return string.Join(string.Empty, Array.ConvertAll(bytes, b => b.ToString("X2"))); } static string ByteArrayToHexStringViaStringConcatArrayConvertAll(byte[] bytes) { return string.Concat(Array.ConvertAll(bytes, b => b.ToString("X2"))); } static string ByteArrayToHexStringViaBitConverter(byte[] bytes) { string hex = BitConverter.ToString(bytes); return hex.Replace("-",""); } static string ByteArrayToHexStringViaStringBuilderAggregateByteToString(byte[] bytes) { return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.Append(b.ToString("X2"))).ToString(); } static string ByteArrayToHexStringViaStringBuilderForEachByteToString(byte[] bytes) { StringBuilder hex = new StringBuilder(bytes.Length * 2); foreach (byte b in bytes) hex.Append(b.ToString("X2")); return hex.ToString(); } static string ByteArrayToHexStringViaStringBuilderAggregateAppendFormat(byte[] bytes) { return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.AppendFormat("{0:X2}", b)).ToString(); } static string ByteArrayToHexStringViaStringBuilderForEachAppendFormat(byte[] bytes) { StringBuilder hex = new StringBuilder(bytes.Length * 2); foreach (byte b in bytes) hex.AppendFormat("{0:X2}", b); return hex.ToString(); } static string ByteArrayToHexViaByteManipulation(byte[] bytes) { char[] c = new char[bytes.Length * 2]; byte b; for (int i = 0; i < bytes.Length; i++) { b = ((byte)(bytes[i] >> 4)); c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30); b = ((byte)(bytes[i] & 0xF)); c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30); } return new string(c); } static string ByteArrayToHexViaByteManipulation2(byte[] bytes) { char[] c = new char[bytes.Length * 2]; int b; for (int i = 0; i < bytes.Length; i++) { b = bytes[i] >> 4; c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); b = bytes[i] & 0xF; c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); } return new string(c); } static string ByteArrayToHexViaSoapHexBinary(byte[] bytes) { SoapHexBinary soapHexBinary = new SoapHexBinary(bytes); return soapHexBinary.ToString(); } static string ByteArrayToHexViaLookupAndShift(byte[] bytes) { StringBuilder result = new StringBuilder(bytes.Length * 2); string hexAlphabet ="0123456789ABCDEF"; foreach (byte b in bytes) { result.Append(hexAlphabet[(int)(b >> 4)]); result.Append(hexAlphabet[(int)(b & 0xF)]); } return result.ToString(); } static readonly uint* _lookup32UnsafeP = (uint*)GCHandle.Alloc(_Lookup32, GCHandleType.Pinned).AddrOfPinnedObject(); static string ByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes) { var lookupP = _lookup32UnsafeP; var result = new string((char)0, bytes.Length * 2); fixed (byte* bytesP = bytes) fixed (char* resultP = result) { uint* resultP2 = (uint*)resultP; for (int i = 0; i < bytes.Length; i++) { resultP2[i] = lookupP[bytesP[i]]; } } return result; } static uint[] _Lookup32 = Enumerable.Range(0, 255).Select(i => { string s = i.ToString("X2"); return ((uint)s[0]) + ((uint)s[1] << 16); }).ToArray(); static string ByteArrayToHexViaLookupPerByte(byte[] bytes) { var result = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { var val = _Lookup32[bytes[i]]; result[2*i] = (char)val; result[2*i + 1] = (char) (val >> 16); } return new string(result); } static string ByteArrayToHexViaLookup(byte[] bytes) { string[] hexStringTable = new string[] { "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F", "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F", "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F", "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F", "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F", "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F", "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F", "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F", "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F", "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F", "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF", "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF", "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF", "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF", "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF", "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF", }; StringBuilder result = new StringBuilder(bytes.Length * 2); foreach (byte b in bytes) { result.Append(hexStringTable[b]); } return result.ToString(); } |
更新(2010-01-13)
在分析中加入了瓦利德的答案。相当快。好的。更新(2011-10-05)
为完整性增加了
测试报告包括更多的变体,如
此外,Mykroff的
增加了codesinchaos的字节操作答案,它占据了第一位(在大的文本块上有很大的空白)。好的。更新(2013-05-23)
添加了内森·莫尼瓦齐里的查找答案和布莱恩·兰伯特博客中的变体。两者都相当快,但没有在我使用的测试机上领先(AMD Phenom 9750)。好的。更新(2014-07-31)
添加了@codesinchaos新的基于字节的查找答案。它似乎在句子测试和全文测试中都处于领先地位。好的。更新(2015-08-20)
在这个答案的repo中添加了airbreather的优化和
有一个类被称为soaphexbinary exactly那是你想要的。 </P >
1 2 3 4 5 6 7 8 9 10 11 12 13 | using System.Runtime.Remoting.Metadata.W3cXsd2001; public static byte[] GetStringToBytes(string value) { SoapHexBinary shb = SoapHexBinary.Parse(value); return shb.Value; } public static string GetBytesToString(byte[] value) { SoapHexBinary shb = new SoapHexBinary(value); return shb.ToString(); } |
在编写加密代码时,通常要避免依赖数据的分支和表查找,以确保运行时不依赖于数据,因为依赖数据的计时可能导致侧通道攻击。
速度也很快。
1 2 3 4 5 6 7 8 9 10 11 12 |
ph'nglui mglw'nafh cthulhu r'lyeh wgah'nagl fhtagn
Abandon all hope, ye who enter here
一个奇怪的小摆弄的解释:
进一步考虑:
- 我没有使用第二个循环变量来索引
c ,因为测量表明从i 计算它更便宜。 - 使用
i < bytes.Length 作为循环的上界,允许抖动消除bytes[i] 上的边界检查,所以我选择了这个变体。 - 使
b 为int允许从字节到字节不必要的转换。
如果你想更多的灵活性比
1 | String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2"))); |
或者,如果你是使用.net 4.0: </P >
1 | String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2"))); |
(《latter虔诚的评论对原创文章。) </P >
你可以用bitconverter.tostring方法: </P >
1 2 | byte[] bytes = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256} Console.WriteLine( BitConverter.ToString(bytes)); |
输出: </P >
00-01-02-04-08-10-20-40-80-FF
更多信息:bitconverter.tostring方法(字节[ ]) </P >
另一种基于查找表的方法。每个字节只使用一个查找表,而不是每个半字节使用一个查找表。
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 | private static readonly uint[] _lookup32 = CreateLookup32(); private static uint[] CreateLookup32() { var result = new uint[256]; for (int i = 0; i < 256; i++) { string s=i.ToString("X2"); result[i] = ((uint)s[0]) + ((uint)s[1] << 16); } return result; } private static string ByteArrayToHexViaLookup32(byte[] bytes) { var lookup32 = _lookup32; var result = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { var val = lookup32[bytes[i]]; result[2*i] = (char)val; result[2*i + 1] = (char) (val >> 16); } return new string(result); } |
我还使用查阅表格中的
根据编译目标(x86、x64),这些对象要么具有大致相同的性能,要么略慢于此变体。
为了更高的性能,它的
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 | private static readonly uint[] _lookup32Unsafe = CreateLookup32Unsafe(); private static readonly uint* _lookup32UnsafeP = (uint*)GCHandle.Alloc(_lookup32Unsafe,GCHandleType.Pinned).AddrOfPinnedObject(); private static uint[] CreateLookup32Unsafe() { var result = new uint[256]; for (int i = 0; i < 256; i++) { string s=i.ToString("X2"); if(BitConverter.IsLittleEndian) result[i] = ((uint)s[0]) + ((uint)s[1] << 16); else result[i] = ((uint)s[1]) + ((uint)s[0] << 16); } return result; } public static string ByteArrayToHexViaLookup32Unsafe(byte[] bytes) { var lookupP = _lookup32UnsafeP; var result = new char[bytes.Length * 2]; fixed(byte* bytesP = bytes) fixed (char* resultP = result) { uint* resultP2 = (uint*)resultP; for (int i = 0; i < bytes.Length; i++) { resultP2[i] = lookupP[bytesP[i]]; } } return new string(result); } |
或者,如果您认为直接写入字符串是可以接受的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static string ByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes) { var lookupP = _lookup32UnsafeP; var result = new string((char)0, bytes.Length * 2); fixed (byte* bytesP = bytes) fixed (char* resultP = result) { uint* resultP2 = (uint*)resultP; for (int i = 0; i < bytes.Length; i++) { resultP2[i] = lookupP[bytesP[i]]; } } return result; } |
现在的问题是一样的encountered今日,我在洞里传出这样的代码: </P >
1 2 3 4 5 6 7 8 9 10 11 12 13 | private static string ByteArrayToHex(byte[] barray) { char[] c = new char[barray.Length * 2]; byte b; for (int i = 0; i < barray.Length; ++i) { b = ((byte)(barray[i] >> 4)); c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30); b = ((byte)(barray[i] & 0xF)); c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30); } return new string(c); } |
源:论坛的帖子[ ]字节的十六进制字符串阵列"(见《后村pzahra)。在一个小的修改的代码去解除"0x前缀。 </P >
在做了一些性能测试的代码和它是几乎八个时代比使用bitconverter.tostring(阿姨)(在fastest根据patridge的邮件)。 </P >
这是对Tomalak高度流行的答案(以及随后的编辑)第4版的回答。好的。
我将说明这个编辑是错误的,并解释为什么它可以被恢复。在这个过程中,您可能会学到一些关于内部的东西,并看到另外一个例子,说明什么是提前优化,以及它如何影响您。好的。
tl;dr:只需使用
警告:如果在框架中实现
一般来说,我不太喜欢说"不要过早优化",因为没人知道"过早"是什么时候。在决定是否优化时,您必须考虑的唯一一件事是:"我是否有时间和资源来研究优化方法?"如果你不这样做,那就太早了,等到你的项目更成熟或者你需要性能(如果有真正的需求,那么你就有时间)。同时,做一些最简单的事情来代替。好的。
原代码:好的。
1 2 3 4 5 6 7 8 | public static byte[] HexadecimalStringToByteArray_Original(string input) { var outputLength = input.Length / 2; var output = new byte[outputLength]; for (var i = 0; i < outputLength; i++) output[i] = Convert.ToByte(input.Substring(i * 2, 2), 16); return output; } |
修订版4:好的。
1 2 3 4 5 6 7 8 9 10 11 | public static byte[] HexadecimalStringToByteArray_Rev4(string input) { var outputLength = input.Length / 2; var output = new byte[outputLength]; using (var sr = new StringReader(input)) { for (var i = 0; i < outputLength; i++) output[i] = Convert.ToByte(new string(new char[2] { (char)sr.Read(), (char)sr.Read() }), 16); } return output; } |
修订版避免使用
Edit: you can improve performance for long strings by using a single
pass parser, like so:Ok.
好吧,看看
不过,它确实分配了一个新字符串,但是无论如何,您需要分配一个字符串来传递给
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static byte[] HexadecimalStringToByteArray(string input) { var outputLength = input.Length / 2; var output = new byte[outputLength]; var numeral = new char[2]; using (var sr = new StringReader(input)) { for (var i = 0; i < outputLength; i++) { numeral[0] = (char)sr.Read(); numeral[1] = (char)sr.Read(); output[i] = Convert.ToByte(new string(numeral), 16); } } return output; } |
每个十六进制的
但是,为什么要打两次电话给
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static byte[] HexadecimalStringToByteArray(string input) { var outputLength = input.Length / 2; var output = new byte[outputLength]; var numeral = new char[2]; using (var sr = new StringReader(input)) { for (var i = 0; i < outputLength; i++) { var read = sr.Read(numeral, 0, 2); Debug.Assert(read == 2); output[i] = Convert.ToByte(new string(numeral), 16); } } return output; } |
剩下的就是一个字符串阅读器,它唯一增加的"值"是一个并行索引(内部
如果您想知道
因此,已经移除字符串阅读器,并自己调用
1 2 3 4 5 6 7 8 9 10 11 12 | public static byte[] HexadecimalStringToByteArray(string input) { var outputLength = input.Length / 2; var output = new byte[outputLength]; var numeral = new char[2]; for (int i = 0, j = 0; i < outputLength; i++, j += 2) { input.CopyTo(j, numeral, 0, 2); output[i] = Convert.ToByte(new string(numeral), 16); } return output; } |
你真的需要一个与
1 2 3 4 5 6 7 8 9 10 11 12 | public static byte[] HexadecimalStringToByteArray_BestEffort(string input) { var outputLength = input.Length / 2; var output = new byte[outputLength]; var numeral = new char[2]; for (int i = 0; i < outputLength; i++) { input.CopyTo(i * 2, numeral, 0, 2); output[i] = Convert.ToByte(new string(numeral), 16); } return output; } |
现在的解决方案是什么样子的?与开始时完全相同,只是不使用
事实上,如果您再次查看
- 最坏的情况:一个快速分配,一个快速复制。
- 最佳案例:没有分配,没有复制。
手工法好的。
- 最坏情况:两个正常分配,一个正常复制,一个快速复制。
- 最佳情况:一个正常分配,一个正常拷贝。
结论?如果你想使用
注意,如果您不需要极端的性能,使用
如果有一个
我怀疑那些通过"避免使用
免责声明:我还没有对框架的最新版本进行反编译,以验证引用源是否是最新的,我认为是最新的。好的。
现在,这一切听起来很好,很有逻辑性,如果你能做到这一点,希望甚至是显而易见的。但这是真的吗?好的。
1 2 3 4 5 6 7 8 9 10 | Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz Cores: 8 Current Clock Speed: 2600 Max Clock Speed: 2600 -------------------- Parsing hexadecimal string into an array of bytes -------------------- HexadecimalStringToByteArray_Original: 7,777.09 average ticks (over 10000 runs), 1.2X HexadecimalStringToByteArray_BestEffort: 8,550.82 average ticks (over 10000 runs), 1.1X HexadecimalStringToByteArray_Rev4: 9,218.03 average ticks (over 10000 runs), 1.0X |
对!好的。
为板凳框架提供支撑,很容易破解。使用的输入是以下sha-1哈希重复5000次以生成100000字节长的字符串。好的。
1 | 209113288F93A9AB8E474EA78D899AFDBB874355 |
玩得高兴!(但要适度优化。)好的。好啊。
这个问题也可以用查找表来解决。这将需要编码器和解码器都有少量的静态内存。然而,这种方法将很快:
- 编码器表512字节或1024字节(两次大小写同时为大写和小写是需要的)
- 解码器表256字节或64 kib(单字符查找或双字符查找)
我的解决方案使用1024字节作为编码表,使用256字节进行解码。
译码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 | private static readonly byte[] LookupTable = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; private static byte Lookup(char c) { var b = LookupTable[c]; if (b == 255) throw new IOException("Expected a hex character, got" + c); return b; } public static byte ToByte(char[] chars, int offset) { return (byte)(Lookup(chars[offset]) << 4 | Lookup(chars[offset + 1])); } |
编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private static readonly char[][] LookupTableUpper; private static readonly char[][] LookupTableLower; static Hex() { LookupTableLower = new char[256][]; LookupTableUpper = new char[256][]; for (var i = 0; i < 256; i++) { LookupTableLower[i] = i.ToString("x2").ToCharArray(); LookupTableUpper[i] = i.ToString("X2").ToCharArray(); } } public static char[] ToCharLower(byte[] b, int bOffset) { return LookupTableLower[b[bOffset]]; } public static char[] ToCharUpper(byte[] b, int bOffset) { return LookupTableUpper[b[bOffset]]; } |
比较
1 2 3 4 5 | StringBuilderToStringFromBytes: 106148 BitConverterToStringFromBytes: 15783 ArrayConvertAllToStringFromBytes: 54290 ByteManipulationToCharArray: 8444 TableBasedToCharArray: 5651 * |
*这个解决方案
注释在解码ioexception和indexoutofrangeexception期间,可能会发生异常(如果字符的值太高>256)。应该实现流或数组的反/编码方法,这只是概念验证。
由@codesinchaos补充答案(反向法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static byte[] HexToByteUsingByteManipulation(string s) { byte[] bytes = new byte[s.Length / 2]; for (int i = 0; i < bytes.Length; i++) { int hi = s[i*2] - 65; hi = hi + 10 + ((hi >> 31) & 7); int lo = s[i*2 + 1] - 65; lo = lo + 10 + ((lo >> 31) & 7) & 0x0f; bytes[i] = (byte) (lo | hi << 4); } return bytes; } |
说明:
对于'0'..'9',它与
对于‘a’…‘f’,它是
对于‘a’…‘f’,我们必须用大数字,所以我们必须从默认版本中减去32,通过使用
65是
48是
7是ASCII表(
这是一个大的邮件。在该类的溶液。在没有计算机patridge馏通的测试,但它表明quite是固定的。在"逆向的过程也必要的十六进制字符串,转换到一个字节数组,所以它作为一wrote逆转(Waleed的溶液。不确定的,如果它的任何阿姨比Tomalak的原始溶液。再次,在没有标注的逆向过程运行的测试patridge通指。 </P >
1 2 3 4 5 6 7 8 9 10 11 12 | private byte[] HexStringToByteArray(string hexString) { int hexStringLength = hexString.Length; byte[] b = new byte[hexStringLength / 2]; for (int i = 0; i < hexStringLength; i += 2) { int topChar = (hexString[i] > 0x40 ? hexString[i] - 0x37 : hexString[i] - 0x30) << 4; int bottomChar = hexString[i + 1] > 0x40 ? hexString[i + 1] - 0x37 : hexString[i + 1] - 0x30; b[i / 2] = Convert.ToByte(topChar + bottomChar); } return b; } |
为什么让它配合物?这是简单的在视觉和nbsp;工作室及nbsp;2008年: </P >
#:C </P >
1 | string hex = BitConverter.ToString(YourByteArray).Replace("-",""); |
VB。 </P >
1 | Dim hex As String = BitConverter.ToString(YourByteArray).Replace("-","") |
这里并没有大量的答案,但是我发现了一个相当理想的十六进制字符串解析器的简单实现(比公认的要好大约4.5倍)。首先,我的测试输出(第一批是我的实现):
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 | Give me that string: 04c63f7842740c77e545bb0b2ade90b384f119f6ab57b680b7aa575a2f40939f Time to parse 100,000 times: 50.4192 ms Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58= BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5 7-B6-80-B7-AA-57-5A-2F-40-93-9F Accepted answer: (StringToByteArray) Time to parse 100000 times: 233.1264ms Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58= BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5 7-B6-80-B7-AA-57-5A-2F-40-93-9F With Mono's implementation: Time to parse 100000 times: 777.2544ms Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58= BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5 7-B6-80-B7-AA-57-5A-2F-40-93-9F With SoapHexBinary: Time to parse 100000 times: 845.1456ms Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58= BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5 7-B6-80-B7-AA-57-5A-2F-40-93-9F |
base64和"bitconverter'd"行用于测试正确性。注意它们是相等的。
实施情况:
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 | public static byte[] ToByteArrayFromHex(string hexString) { if (hexString.Length % 2 != 0) throw new ArgumentException("String must have an even length"); var array = new byte[hexString.Length / 2]; for (int i = 0; i < hexString.Length; i += 2) { array[i/2] = ByteFromTwoChars(hexString[i], hexString[i + 1]); } return array; } private static byte ByteFromTwoChars(char p, char p_2) { byte ret; if (p <= '9' && p >= '0') { ret = (byte) ((p - '0') << 4); } else if (p <= 'f' && p >= 'a') { ret = (byte) ((p - 'a' + 10) << 4); } else if (p <= 'F' && p >= 'A') { ret = (byte) ((p - 'A' + 10) << 4); } else throw new ArgumentException("Char is not a hex digit:" + p,"p"); if (p_2 <= '9' && p_2 >= '0') { ret |= (byte) ((p_2 - '0')); } else if (p_2 <= 'f' && p_2 >= 'a') { ret |= (byte) ((p_2 - 'a' + 10)); } else if (p_2 <= 'F' && p_2 >= 'A') { ret |= (byte) ((p_2 - 'A' + 10)); } else throw new ArgumentException("Char is not a hex digit:" + p_2,"p_2"); return ret; } |
我尝试了一些关于
(我承认这回答了一半的问题。我觉得string->byte[]转换被低估了,而byte[]->string角度似乎被很好地覆盖了。因此,这个答案。)
安全版本:
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 | public static class HexHelper { [System.Diagnostics.Contracts.Pure] public static string ToHex(this byte[] value) { if (value == null) throw new ArgumentNullException("value"); const string hexAlphabet = @"0123456789ABCDEF"; var chars = new char[checked(value.Length * 2)]; unchecked { for (int i = 0; i < value.Length; i++) { chars[i * 2] = hexAlphabet[value[i] >> 4]; chars[i * 2 + 1] = hexAlphabet[value[i] & 0xF]; } } return new string(chars); } [System.Diagnostics.Contracts.Pure] public static byte[] FromHex(this string value) { if (value == null) throw new ArgumentNullException("value"); if (value.Length % 2 != 0) throw new ArgumentException("Hexadecimal value length must be even.","value"); unchecked { byte[] result = new byte[value.Length / 2]; for (int i = 0; i < result.Length; i++) { // 0(48) - 9(57) -> 0 - 9 // A(65) - F(70) -> 10 - 15 int b = value[i * 2]; // High 4 bits. int val = ((b - '0') + ((('9' - b) >> 31) & -7)) << 4; b = value[i * 2 + 1]; // Low 4 bits. val += (b - '0') + ((('9' - b) >> 31) & -7); result[i] = checked((byte)val); } return result; } } } |
不安全的版本,为那些喜欢性能和不怕不安全的人。六角速度约为35%,六角速度约为10%。
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 | public static class HexUnsafeHelper { [System.Diagnostics.Contracts.Pure] public static unsafe string ToHex(this byte[] value) { if (value == null) throw new ArgumentNullException("value"); const string alphabet = @"0123456789ABCDEF"; string result = new string(' ', checked(value.Length * 2)); fixed (char* alphabetPtr = alphabet) fixed (char* resultPtr = result) { char* ptr = resultPtr; unchecked { for (int i = 0; i < value.Length; i++) { *ptr++ = *(alphabetPtr + (value[i] >> 4)); *ptr++ = *(alphabetPtr + (value[i] & 0xF)); } } } return result; } [System.Diagnostics.Contracts.Pure] public static unsafe byte[] FromHex(this string value) { if (value == null) throw new ArgumentNullException("value"); if (value.Length % 2 != 0) throw new ArgumentException("Hexadecimal value length must be even.","value"); unchecked { byte[] result = new byte[value.Length / 2]; fixed (char* valuePtr = value) { char* valPtr = valuePtr; for (int i = 0; i < result.Length; i++) { // 0(48) - 9(57) -> 0 - 9 // A(65) - F(70) -> 10 - 15 int b = *valPtr++; // High 4 bits. int val = ((b - '0') + ((('9' - b) >> 31) & -7)) << 4; b = *valPtr++; // Low 4 bits. val += (b - '0') + ((('9' - b) >> 31) & -7); result[i] = checked((byte)val); } } return result; } } } |
顺便说一句对于每次调用的转换函数错误时初始化字母表的基准测试,字母表必须是const(对于字符串)或static readonly(对于char[])。然后,基于字母表的字节[]到字符串的转换速度和字节操作版本一样快。
当然,测试必须在发行版(带优化)中编译,并关闭调试选项"抑制JIT优化"(如果代码必须可调试,则与"仅启用我的代码"相同)。
Waleed EISSA代码的反函数(十六进制字符串到字节数组):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static byte[] HexToBytes(this string hexString) { byte[] b = new byte[hexString.Length / 2]; char c; for (int i = 0; i < hexString.Length / 2; i++) { c = hexString[i * 2]; b[i] = (byte)((c < 0x40 ? c - 0x30 : (c < 0x47 ? c - 0x37 : c - 0x57)) << 4); c = hexString[i * 2 + 1]; b[i] += (byte)(c < 0x40 ? c - 0x30 : (c < 0x47 ? c - 0x37 : c - 0x57)); } return b; } |
Waleed EISSA功能,支持小写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static string BytesToHex(this byte[] barray, bool toLowerCase = true) { byte addByte = 0x37; if (toLowerCase) addByte = 0x57; char[] c = new char[barray.Length * 2]; byte b; for (int i = 0; i < barray.Length; ++i) { b = ((byte)(barray[i] >> 4)); c[i * 2] = (char)(b > 9 ? b + addByte : b + 0x30); b = ((byte)(barray[i] & 0xF)); c[i * 2 + 1] = (char)(b > 9 ? b + addByte : b + 0x30); } return new string(c); } |
从微软的开发人员,一个好的,简单的转换: </P >
1 2 3 4 5 6 7 | public static string ByteArrayToString(byte[] ba) { // Concatenate the bytes into one long string return ba.Aggregate(new StringBuilder(32), (sb, b) => sb.Append(b.ToString("X2")) ).ToString(); } |
在《冰上清洁的契约,绩效的人,会对它的使用enumerators尖叫。你可以只山羊的峰值性能和改进的版本的原始tomolak答案: </P >
1 2 3 4 5 6 7 8 9 | public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); for(int i=0; i < ga.Length; i++) // <-- Use for loop is faster than foreach hex.Append(ba[i].ToString("X2")); // <-- ToString is faster than AppendFormat return hex.ToString(); } |
这是fastest大学所有的套路,在我见过的父亲在这里发布"。不要把我的话,就用它。性能测试的实现和它的每inspect CIL代码为自己。 </P >
扩展方法(disclaimer:完全untested代码,BTW:…………………) </P >
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static class ByteExtensions { public static string ToHexString(this byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) { hex.AppendFormat("{0:x2}", b); } return hex.ToString(); } } |
等。使用指三部Tomalak的解决方案(与一个负载的市场推广方法上的字符串)。 </P >
在条款的速度表明,这是更好的比任何事情都在这里: </P >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static string ToHexString(byte[] data) { byte b; int i, j, k; int l = data.Length; char[] r = new char[l * 2]; for (i = 0, j = 0; i < l; ++i) { b = data[i]; k = b >> 4; r[j++] = (char)(k > 9 ? k + 0x37 : k + 0x30); k = b & 15; r[j++] = (char)(k > 9 ? k + 0x37 : k + 0x30); } return new string(r); } |
我将进入这个有点摆弄的竞争,因为我有一个答案,也使用有点摆弄解码十六进制。注意,使用字符数组可能更快,因为调用
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 80 81 82 | public static String ToHex (byte[] data) { int dataLength = data.Length; // pre-create the stringbuilder using the length of the data * 2, precisely enough StringBuilder sb = new StringBuilder (dataLength * 2); for (int i = 0; i < dataLength; i++) { int b = data [i]; // check using calculation over bits to see if first tuple is a letter // isLetter is zero if it is a digit, 1 if it is a letter int isLetter = (b >> 7) & ((b >> 6) | (b >> 5)) & 1; // calculate the code using a multiplication to make up the difference between // a digit character and an alphanumerical character int code = '0' + ((b >> 4) & 0xF) + isLetter * ('A' - '9' - 1); // now append the result, after casting the code point to a character sb.Append ((Char)code); // do the same with the lower (less significant) tuple isLetter = (b >> 3) & ((b >> 2) | (b >> 1)) & 1; code = '0' + (b & 0xF) + isLetter * ('A' - '9' - 1); sb.Append ((Char)code); } return sb.ToString (); } public static byte[] FromHex (String hex) { // pre-create the array int resultLength = hex.Length / 2; byte[] result = new byte[resultLength]; // set validity = 0 (0 = valid, anything else is not valid) int validity = 0; int c, isLetter, value, validDigitStruct, validDigit, validLetterStruct, validLetter; for (int i = 0, hexOffset = 0; i < resultLength; i++, hexOffset += 2) { c = hex [hexOffset]; // check using calculation over bits to see if first char is a letter // isLetter is zero if it is a digit, 1 if it is a letter (upper & lowercase) isLetter = (c >> 6) & 1; // calculate the tuple value using a multiplication to make up the difference between // a digit character and an alphanumerical character // minus 1 for the fact that the letters are not zero based value = ((c & 0xF) + isLetter * (-1 + 10)) << 4; // check validity of all the other bits validity |= c >> 7; // changed to >>, maybe not OK, use UInt? validDigitStruct = (c & 0x30) ^ 0x30; validDigit = ((c & 0x8) >> 3) * (c & 0x6); validity |= (isLetter ^ 1) * (validDigitStruct | validDigit); validLetterStruct = c & 0x18; validLetter = (((c - 1) & 0x4) >> 2) * ((c - 1) & 0x2); validity |= isLetter * (validLetterStruct | validLetter); // do the same with the lower (less significant) tuple c = hex [hexOffset + 1]; isLetter = (c >> 6) & 1; value ^= (c & 0xF) + isLetter * (-1 + 10); result [i] = (byte)value; // check validity of all the other bits validity |= c >> 7; // changed to >>, maybe not OK, use UInt? validDigitStruct = (c & 0x30) ^ 0x30; validDigit = ((c & 0x8) >> 3) * (c & 0x6); validity |= (isLetter ^ 1) * (validDigitStruct | validDigit); validLetterStruct = c & 0x18; validLetter = (((c - 1) & 0x4) >> 2) * ((c - 1) & 0x2); validity |= isLetter * (validLetterStruct | validLetter); } if (validity != 0) { throw new ArgumentException ("Hexadecimal encoding incorrect for input" + hex); } return result; } |
从Java代码转换。
在做注释你的代码suggested山羊到工作,olipro。
在做,但是有一些成功的一些提示的完整信息村以虔诚的waleeds hammering法典和本在一起。它的丑陋的混蛋冰雹,但它表明到工作和performs AT 1 / 3的时间相比,给他人(根据我的测试,以patridges测试机制。depending在线输入尺寸。开关的方位吗?到单独的0~9的第一时间就可能收益(因为有咯阿姨的结果是更多的比数的信。 </P >
1 2 3 4 5 6 7 8 9 10 11 | public static byte[] StringToByteArray2(string hex) { byte[] bytes = new byte[hex.Length/2]; int bl = bytes.Length; for (int i = 0; i < bl; ++i) { bytes[i] = (byte)((hex[2 * i] > 'F' ? hex[2 * i] - 0x57 : hex[2 * i] > '9' ? hex[2 * i] - 0x37 : hex[2 * i] - 0x30) << 4); bytes[i] |= (byte)(hex[2 * i + 1] > 'F' ? hex[2 * i + 1] - 0x57 : hex[2 * i + 1] > '9' ? hex[2 * i + 1] - 0x37 : hex[2 * i + 1] - 0x30); } return bytes; } |
这个版本的BytearraytoHexviaBytemanipulation可以更快。
从我的报告:
- 按时间顺序排列:1,68平均滴答数(超过1000次),17,5x
- 按时间顺序排列2:1,73平均滴答数(超过1000次),16,9x
- 通过发射率:2,90平均滴答数(超过1000次),10,1X
- 通过EarrayToHexViaLookupandShift:3,22平均计时周期(超过1000次),9,1X
…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15static private readonly char[] hexAlphabet = new char[]
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
static string ByteArrayToHexViaByteManipulation3(byte[] bytes)
{
char[] c = new char[bytes.Length * 2];
byte b;
for (int i = 0; i < bytes.Length; i++)
{
b = ((byte)(bytes[i] >> 4));
c[i * 2] = hexAlphabet[b];
b = ((byte)(bytes[i] & 0xF));
c[i * 2 + 1] = hexAlphabet[b];
}
return new string(c);
}
我认为这是一个优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 | static private readonly char[] hexAlphabet = new char[] {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; static string ByteArrayToHexViaByteManipulation4(byte[] bytes) { char[] c = new char[bytes.Length * 2]; for (int i = 0, ptr = 0; i < bytes.Length; i++, ptr += 2) { byte b = bytes[i]; c[ptr] = hexAlphabet[b >> 4]; c[ptr + 1] = hexAlphabet[b & 0xF]; } return new string(c); } |
对于性能,我将使用Drphrozens解决方案。解码器的一个微小的优化可能是使用一个表来为每个字符去掉"<<4"。
显然,这两个方法调用是昂贵的。如果对输入或输出数据进行某种检查(可以是CRC、校验和或其他类型),则可以跳过
使用
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 | private static readonly byte[] LookupTableLow = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; private static readonly byte[] LookupTableHigh = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; private static byte LookupLow(char c) { var b = LookupTableLow[c]; if (b == 255) throw new IOException("Expected a hex character, got" + c); return b; } private static byte LookupHigh(char c) { var b = LookupTableHigh[c]; if (b == 255) throw new IOException("Expected a hex character, got" + c); return b; } public static byte ToByte(char[] chars, int offset) { return (byte)(LookupHigh(chars[offset++]) | LookupLow(chars[offset])); } |
这只是我的头顶,没有测试或基准。
另一种多样性的变化是:
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 | public static byte[] FromHexString(string src) { if (String.IsNullOrEmpty(src)) return null; int index = src.Length; int sz = index / 2; if (sz <= 0) return null; byte[] rc = new byte[sz]; while (--sz >= 0) { char lo = src[--index]; char hi = src[--index]; rc[sz] = (byte)( ( (hi >= '0' && hi <= '9') ? hi - '0' : (hi >= 'a' && hi <= 'f') ? hi - 'a' + 10 : (hi >= 'A' && hi <= 'F') ? hi - 'A' + 10 : 0 ) << 4 | ( (lo >= '0' && lo <= '9') ? lo - '0' : (lo >= 'a' && lo <= 'f') ? lo - 'a' + 10 : (lo >= 'A' && lo <= 'F') ? lo - 'A' + 10 : 0 ) ); } return rc; } |
和其他inserting到SQL字符串(如果你没有使用命令行参数): </P >
1 2 3 4 | public static String ByteArrayToSQLHexString(byte[] Source) { return ="0x" + BitConverter.ToString(Source).Replace("-",""); } |
这是我的照片。我创建了一对扩展类来扩展字符串和字节。在大型文件测试中,性能与字节操作2相当。
下面的tohexstring代码是查找和移位算法的优化实现。它几乎与Behrooz的方法相同,但事实证明使用
在我的机器上,它排在字节操作2之后的第二位,是非常可读的代码。以下测试结果也很重要:
tohexStringChararrayWithChararrayLookup:41589.69平均刻度(超过1000次运行),1.5xToHexStringCharArrayWithStringLookup:50764.06平均刻度(超过1000次运行),1.2xToHexStringBuilderWithCharArrayLookup:62812.87平均计时周期(超过1000次运行),1.0倍
根据上述结果,可以得出以下结论:
代码如下:
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 | using System; namespace ConversionExtensions { public static class ByteArrayExtensions { private readonly static char[] digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public static string ToHexString(this byte[] bytes) { char[] hex = new char[bytes.Length * 2]; int index = 0; foreach (byte b in bytes) { hex[index++] = digits[b >> 4]; hex[index++] = digits[b & 0x0F]; } return new string(hex); } } } using System; using System.IO; namespace ConversionExtensions { public static class StringExtensions { public static byte[] ToBytes(this string hexString) { if (!string.IsNullOrEmpty(hexString) && hexString.Length % 2 != 0) { throw new FormatException("Hexadecimal string must not be empty and must contain an even number of digits to be valid."); } hexString = hexString.ToUpperInvariant(); byte[] data = new byte[hexString.Length / 2]; for (int index = 0; index < hexString.Length; index += 2) { int highDigitValue = hexString[index] <= '9' ? hexString[index] - '0' : hexString[index] - 'A' + 10; int lowDigitValue = hexString[index + 1] <= '9' ? hexString[index + 1] - '0' : hexString[index + 1] - 'A' + 10; if (highDigitValue < 0 || lowDigitValue < 0 || highDigitValue > 15 || lowDigitValue > 15) { throw new FormatException("An invalid digit was encountered. Valid hexadecimal digits are 0-9 and A-F."); } else { byte value = (byte)((highDigitValue << 4) | (lowDigitValue & 0x0F)); data[index / 2] = value; } } return data; } } } |
下面是我在机器上把代码放入@patricge的测试项目时得到的测试结果。我还添加了一个从十六进制转换为字节数组的测试。执行我的代码的测试运行是由earraytohexviaoptimizedlookupandshift和hextobytearrayviabyemanipulation进行的。从XXXX中提取了转化成己糖。hextobytearrayviasaphexbinary是@mykroff的答案。
Intel Pentium III Xeon processor
1
2
3 Cores: 4 <br/>
Current Clock Speed: 1576 <br/>
Max Clock Speed: 3092 <br/>Converting array of bytes into hexadecimal string representation
ByteArrayToHexViaByteManipulation2: 39,366.64 average ticks (over 1000 runs), 22.4X
ByteArrayToHexViaOptimizedLookupAndShift: 41,588.64 average ticks
(over 1000 runs), 21.2XByteArrayToHexViaLookup: 55,509.56 average ticks (over 1000 runs), 15.9X
ByteArrayToHexViaByteManipulation: 65,349.12 average ticks (over 1000 runs), 13.5X
ByteArrayToHexViaLookupAndShift: 86,926.87 average ticks (over 1000
runs), 10.2XByteArrayToHexStringViaBitConverter: 139,353.73 average
ticks (over 1000 runs),6.3XByteArrayToHexViaSoapHexBinary: 314,598.77 average ticks (over 1000 runs), 2.8X
ByteArrayToHexStringViaStringBuilderForEachByteToString: 344,264.63
average ticks (over 1000 runs), 2.6XByteArrayToHexStringViaStringBuilderAggregateByteToString: 382,623.44
average ticks (over 1000 runs), 2.3XByteArrayToHexStringViaStringBuilderForEachAppendFormat: 818,111.95
average ticks (over 1000 runs), 1.1XByteArrayToHexStringViaStringConcatArrayConvertAll: 839,244.84 average
ticks (over 1000 runs), 1.1XByteArrayToHexStringViaStringBuilderAggregateAppendFormat: 867,303.98
average ticks (over 1000 runs), 1.0XByteArrayToHexStringViaStringJoinArrayConvertAll: 882,710.28 average
ticks (over 1000 runs), 1.0X
没有针对速度进行优化,但比大多数答案(0.NET 4.0)更具灵敏性:
1 2 3 4 5 6 7 8 9 10 11 | <Extension()> Public Function FromHexToByteArray(hex As String) As Byte() hex = If(hex, String.Empty) If hex.Length Mod 2 = 1 Then hex ="0" & hex Return Enumerable.Range(0, hex.Length \ 2).Select(Function(i) Convert.ToByte(hex.Substring(i * 2, 2), 16)).ToArray End Function <Extension()> Public Function ToHexString(bytes As IEnumerable(Of Byte)) As String Return String.Concat(bytes.Select(Function(b) b.ToString("X2"))) End Function |
另一个快速功能…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private static readonly byte[] HexNibble = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; public static byte[] HexStringToByteArray( string str ) { int byteCount = str.Length >> 1; byte[] result = new byte[byteCount + (str.Length & 1)]; for( int i = 0; i < byteCount; i++ ) result[i] = (byte) (HexNibble[str[i << 1] - 48] << 4 | HexNibble[str[(i << 1) + 1] - 48]); if( (str.Length & 1) != 0 ) result[byteCount] = (byte) HexNibble[str[str.Length - 1] - 48]; return result; } |
另一种方法是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static string ByteToHexBitFiddle(byte[] bytes) { var c = stackalloc char[bytes.Length * 2 + 1]; int b; for (int i = 0; i < bytes.Length; ++i) { b = bytes[i] >> 4; c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); b = bytes[i] & 0xF; c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); } c[bytes.Length * 2 ] = '\0'; return new string(c); } |
两个混搭,将两个半字节操作折叠成一个。
可能是相当有效的版本:
1 2 3 4 5 6 7 8 9 10 |
带比特黑客版本的颓废LINQ:
1 2 3 4 | public static string ByteArrayToString(byte[] ba) { return string.Concat( ba.SelectMany( b => new int[] { b >> 4, b & 0xF }).Select( b => (char)(55 + b + (((b-10)>>31)&-7))) ); } |
和反向:
1 2 3 4 5 6 7 8 9 10 11 | public static byte[] HexStringToByteArray( string s ) { byte[] ab = new byte[s.Length>>1]; for( int i = 0; i < s.Length; i++ ) { int b = s[i]; b = (b - '0') + ((('9' - b)>>31)&-7); ab[i>>1] |= (byte)(b << 4*((i&1)^1)); } return ab; } |
如果绩效因素,这里的其他改进方案: </P >
1 2 3 4 5 6 7 8 9 10 11 12 13 | static readonly char[] _hexDigits ="0123456789abcdef".ToCharArray(); public static string ToHexString(this byte[] bytes) { char[] digits = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { int d1, d2; d1 = Math.DivRem(bytes[i], 16, out d2); digits[2 * i] = _hexDigits[d1]; digits[2 * i + 1] = _hexDigits[d2]; } return new string(digits); } |
它的意思是
它的作用是从字符串到字节数组…
1 2 3 4 5 6 7 8 9 10 11 12 | public static byte[] StrToByteArray(string str) { Dictionary<string, byte> hexindex = new Dictionary<string, byte>(); for (byte i = 0; i < 255; i++) hexindex.Add(i.ToString("X2"), i); List<byte> hexres = new List<byte>(); for (int i = 0; i < str.Length; i += 2) hexres.Add(hexindex[str.Substring(i, 2)]); return hexres.ToArray(); } |
我想它的速度值16个字节。
1 2 3 4 5 6 7 8 9 10 11 | static char[] hexes = new char[]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; public static string ToHexadecimal (this byte[] Bytes) { char[] Result = new char[Bytes.Length << 1]; int Offset = 0; for (int i = 0; i != Bytes.Length; i++) { Result[Offset++] = hexes[Bytes[i] >> 4]; Result[Offset++] = hexes[Bytes[i] & 0x0F]; } return new string(Result); } |
还有
下面是一个独立的方法来查看它的工作原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static string ToBinHex(byte[] bytes) { XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); xmlWriterSettings.ConformanceLevel = ConformanceLevel.Fragment; xmlWriterSettings.CheckCharacters = false; xmlWriterSettings.Encoding = ASCIIEncoding.ASCII; MemoryStream memoryStream = new MemoryStream(); using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings)) { xmlWriter.WriteBinHex(bytes, 0, bytes.Length); } return Encoding.ASCII.GetString(memoryStream.ToArray()); } |
下面通过允许本机小写选项扩展了这里的最佳答案,并处理空或空输入,使其成为扩展方法。
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 | /// <summary> /// Converts the byte array to a hex string very fast. Excellent job /// with code lightly adapted from 'community wiki' here: https://stackoverflow.com/a/14333437/264031 /// (the function was originally named: ByteToHexBitFiddle). Now allows a native lowerCase option /// to be input and allows null or empty inputs (null returns null, empty returns empty). /// </summary> public static string ToHexString(this byte[] bytes, bool lowerCase = false) { if (bytes == null) return null; else if (bytes.Length == 0) return""; char[] c = new char[bytes.Length * 2]; int b; int xAddToAlpha = lowerCase ? 87 : 55; int xAddToDigit = lowerCase ? -39 : -7; for (int i = 0; i < bytes.Length; i++) { b = bytes[i] >> 4; c[i * 2] = (char)(xAddToAlpha + b + (((b - 10) >> 31) & xAddToDigit)); b = bytes[i] & 0xF; c[i * 2 + 1] = (char)(xAddToAlpha + b + (((b - 10) >> 31) & xAddToDigit)); } string val = new string(c); return val; } public static string ToHexString(this IEnumerable<byte> bytes, bool lowerCase = false) { if (bytes == null) return null; byte[] arr = bytes.ToArray(); return arr.ToHexString(lowerCase); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | static string ByteArrayToHexViaLookupPerByte2(byte[] bytes) { var result3 = new uint[bytes.Length]; for (int i = 0; i < bytes.Length; i++) result3[i] = _Lookup32[bytes[i]]; var handle = GCHandle.Alloc(result3, GCHandleType.Pinned); try { var result = Marshal.PtrToStringUni(handle.AddrOfPinnedObject(), bytes.Length * 2); return result; } finally { handle.Free(); } } |
我的测试中的这个函数总是不安全实现之后的第二个条目。
不幸的是,测试台不太可靠…如果你多次运行它,列表就会被洗牌,谁知道在不安全的情况下,这是最快的!它不考虑预热、JIT编译时间和GC性能命中。我想重写它以获得更多的信息,但我没有真正的时间。
如果你想山羊"4倍的速度增长wcoenen村"的报告,然后,如果它的注释:
你也可以把它一步步骑和山羊的
带扩展支持的基本解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static class Utils { public static byte[] ToBin(this string hex) { int NumberChars = hex.Length; byte[] bytes = new byte[NumberChars / 2]; for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } public static string ToHex(this byte[] ba) { return BitConverter.ToString(ba).Replace("-",""); } } |
像下面这样使用这个类
1 2 3 |
我怀疑这样的速度会使大多数其他测试失败…
1 2 3 4 5 6 7 | Public Function BufToHex(ByVal buf() As Byte) As String Dim sB As New System.Text.StringBuilder For i As Integer = 0 To buf.Length - 1 sB.Append(buf(i).ToString("x2")) Next i Return sB.ToString End Function |