如何将字节数组转换为十六进制字符串,反之亦然?


当前回答

还有XmlWriter.WriteBinHex(请参见MSDN页面)。如果需要将十六进制字符串放入XML流中,这非常有用。

下面是一个独立的方法来了解它的工作原理:

    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());
    }

其他回答

可以使用从.NET 5开始的Convert.ToHexString。还有一个用于反向操作的方法:Convert.FromHexString。


对于较旧版本的.NET,您可以使用:

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();
}

or:

public static string ByteArrayToString(byte[] ba)
{
  return BitConverter.ToString(ba).Replace("-","");
}

举个例子,这里有更多的方法。

反向转换如下:

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;
}

使用Substring是与Convert.ToByte结合使用的最佳选项。有关详细信息,请参阅此答案。如果需要更好的性能,必须避免Convert.ToByte,然后才能删除SubString。

如果你想得到wcoenen报告的“4倍速度增长”,那么如果不明显:用hex[i]+hex[i+1]替换hex.Substring(i,2)

您还可以再进一步,通过在两个地方使用i++来消除i+=2。

在编写加密代码时,通常避免依赖数据的分支和表查找,以确保运行时不依赖于数据,因为依赖数据的计时可能会导致侧通道攻击。

它也很快。

static string ByteToHexBitFiddle(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);
}

Ph'nglui mglw'naph Cthulhu R'lieh wgah'nagl fhtagn公司


你们这些进入这里的人,放弃一切希望吧

一个奇怪的拨弄解释:

bytes[i]>>4提取字节的高位半字节bytes[i]&0xF提取字节的低位半字节b-10对于值b<10,为<0,将变为十进制数字对于值b>10,为>=0,这将成为从a到F的字母。在有符号32位整数上使用i>>31可以提取符号,这得益于符号扩展。当i<0时为-1,当i>=0时为0。结合2)和3),表明(b-10)>>31将是字母0,数字-1。看看字母的大小写,最后一个被加数变为0,b在10到15的范围内。我们希望将其映射到A(65)到F(70),这意味着添加55('A'-10)。看看数字的情况,我们希望调整最后一个被加数,使其将b从范围0到9映射到范围0(48)到9(57)。这意味着它需要变为-7('0'-55)。现在我们可以乘以7。但由于-1由所有位表示为1,因此我们可以改用&-7,因为(0&-7)==0和(-1&-7)==-7。

进一步考虑:

我没有使用第二个循环变量来索引c,因为测量表明从I计算它更便宜。正好使用i<bytes.Length作为循环的上限允许JITter消除对bytes[i]的边界检查,所以我选择了这个变量。将b设为int允许不必要的从和到字节的转换。

如果您希望比BitConverter更灵活,但不希望使用那些笨重的90年代风格的显式循环,那么您可以这样做:

String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));

或者,如果您使用的是.NET 4.0:

String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));

(后者来自对原帖子的评论。)

用@CodesInChaus补充答案(反向方法)

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;
}

说明:

&0x0f还支持小写字母

hi=hi+10+((hi>>31)&7);与以下内容相同:

hi=ch-65+10+((ch-65)>>31)&7);

对于“0”9’与hi=ch-65+10+7相同;其为hi=ch-48(这是因为0xffffff&7)。

对于“A”F’为hi=ch-65+10;(这是因为0x00000000&7)。

对于“a”我们必须使用大数字,所以我们必须通过使用&0x0f使某些位为0,从默认版本中减去32。

65是“A”的代码

48是“0”的代码

7是ASCII表中“9”和“A”之间的字母数(…456789:;<=>?@ABCD…)。