如何将十六进制转换为字节数组?

10

我从SQL Server复制并粘贴了这些二进制数据,目前无法对其进行查询。

0xBAC893CAB8B7FE03C927417A2A3F6A60BD30FF35E250011CB25507EBFCD5223B

在C#中,我该如何将它转换回字节数组?

9个回答

22

类似这样:

using System;

public static class Parser
{    
    static void Main()
    {
        string hex = "0xBAC893CAB8B7FE03C927417A2A3F6A6"
                     + "0BD30FF35E250011CB25507EBFCD5223B";
        byte[] parsed = ParseHex(hex);
        // Just for confirmation...
        Console.WriteLine(BitConverter.ToString(parsed));
    }

    public static byte[] ParseHex(string hex)
    {
        int offset = hex.StartsWith("0x") ? 2 : 0;
        if ((hex.Length % 2) != 0)
        {
            throw new ArgumentException("Invalid length: " + hex.Length);
        }
        byte[] ret = new byte[(hex.Length-offset)/2];

        for (int i=0; i < ret.Length; i++)
        {
            ret[i] = (byte) ((ParseNybble(hex[offset]) << 4) 
                             | ParseNybble(hex[offset+1]));
            offset += 2;
        }
        return ret;
    }        

    static int ParseNybble(char c)
    {
        if (c >= '0' && c <= '9')
        {
            return c-'0';
        }
        if (c >= 'A' && c <= 'F')
        {
            return c-'A'+10;
        }
        if (c >= 'a' && c <= 'f')
        {
            return c-'a'+10;
        }
        throw new ArgumentException("Invalid hex digit: " + c);
    }
}

(编辑:现在更有效率了- 不需要子字符串...)

可能 ParseNybble 可以更有效率。例如,使用 switch/case 可能更有效率:

    static int ParseNybble(char c)
    {
        switch (c)
        {
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                return c-'0';
            case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
                return c-'A'+10;
            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
                return c-'a'+10;
        }
        throw new ArgumentException("Invalid hex digit: " + c);
    }

或者可能是一个查找数组:

    // Omitted for brevity... I'm sure you get the gist
    private static readonly int[] NybbleLookup = BuildLookup();

    private int ParseNybble(char c)
    {
        if (c > 'f')
        {
            throw new ArgumentException("Invalid hex digit: " + c);
        }
        int ret = NybbleLookup[c];
        if (ret == -1)
        {
            throw new ArgumentException("Invalid hex digit: " + c);
        }
        return ret;
    }

我没有对它们进行基准测试,也不知道哪一个是最快的。但目前的解决方案可能是最简单的。


是的。为什么不呢?它很高效,并且这是您只需要在某个库中编写一次的代码类型。创建 n*2 个字符串以创建长度为 n 的字节数组似乎非常浪费,而另一种选择很容易实现。 - Jon Skeet
+1. 我的代码可能更加“直截了当”,但我认为你的代码更加高效。也许我会加入ParseNybble。将其合并到我的现有代码中应该很容易。 - Erich Mirabal
另外,不必使用substring,您可以在循环的起始值中进行偏移(即i = offset...),长度也一样(如果hex以“0x”开头,则长度为(hex/2)- 1)。 - Erich Mirabal
@Erich:绝对没错。但这会稍微复杂一些...我想保持这个程序相当高效,同时还要简单。不过我会看看能做些什么 :) - Jon Skeet
好的,我忍不住做了一些微调 :) - Jon Skeet
显示剩余2条评论

6
考虑利用已经暴露出十六进制转换能力的框架类,例如XmlReader:
public static byte[] HexToBytes(this string hexEncodedBytes, int start, int end)
{
    int length = end - start;
    const string tagName = "hex";
    string fakeXmlDocument = String.Format("<{1}>{0}</{1}>",
                           hexEncodedBytes.Substring(start, length),
                           tagName);
    var stream = new MemoryStream(Encoding.ASCII.GetBytes(fakeXmlDocument));
    XmlReader reader = XmlReader.Create(stream, new XmlReaderSettings());
    int hexLength = length / 2;
    byte[] result = new byte[hexLength];
    reader.ReadStartElement(tagName);
    reader.ReadContentAsBinHex(result, 0, hexLength);
    return result;
}

使用方法:

string input = "0xBAC893CAB8B7FE03C927417A2A3F6A60BD30FF35E250011CB255";
byte[] bytes = input.HexToBytes(2, input.Length);

4

简单:

string hexnum = "0000000F"; // Represents 15
int value = int.Parse(hexnum, System.Globalization.NumberStyles.HexNumber);

你需要记住的是,对于一个int类型,将十六进制数分成8个十六进制数字的组(每个十六进制数字占4位,CLR int类型占32位,因此每个int类型包含8个数字)。还有一种byte.Parse()方法也可以实现相同的功能,但需要一次传入两个十六进制数字。


你仍然需要转换为byte[]。可以使用BitConverter来完成。如果序列的长度不是sizeof(int)的倍数,该怎么办? - Erich Mirabal
@Erich - 是的,这就是为什么我在最后一句话中也提到了byte.Parse()方法。 - Kevin Anderson
显然,对于问题中指定长度的字符串无效。 - Roman Starkov

3

类似这样的:

    public byte[] ParseHexString(string text)
    {
        if ((text.Length % 2) != 0)
        {
            throw new ArgumentException("Invalid length: " + text.Length);
        }

        if (text.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
        {
            text = text.Substring(2);
        }

        int arrayLength = text.Length / 2;
        byte[] byteArray = new byte[arrayLength];
        for (int i = 0; i < arrayLength; i++)
        {
            byteArray[i] = byte.Parse(text.Substring(i*2, 2), NumberStyles.HexNumber);
        }

        return byteArray;
    }

+1 简单易懂。虽然其他答案可能有更好的字符处理和更好的性能,但这个答案以一种对初学者易于理解的方式解决了问题。 - Andacious

1

实际上,有一种更简单的方法将每次两个字符转换为一个字节:

    /// <summary>
    /// This will convert a hex-encoded string to byte data
    /// </summary>
    /// <param name="hexData">The hex-encoded string to convert</param>
    /// <returns>The bytes that make up the hex string</returns>
    public static byte[] FromHex(string hexData)
    {
        List<byte> data = new List<byte>();
        string byteSet = string.Empty;
        int stringLen = hexData.Length;
        int length = 0;
        for (int i = 0; i < stringLen; i = i + 2)
        {
            length = (stringLen - i) > 1 ? 2 : 1;
            byteSet = hexData.Substring(i, length);

            // try and parse the data
            data.Add(Convert.ToByte(byteSet, 16 /*base*/));
        } // next set

        return data.ToArray();
    }

1
你需要稍微修改一下这个代码(例如跳过前两个字符),但它确实可以处理字符串中的空格:
    /// <summary>
    /// Decodes a hex string, ignoring all non-hex characters, and stores
    /// the decodes series of bytes into the shared buffer. This returns
    /// the number of bytes that were decoded.
    /// <para>Hex characters are [0-9, a-f, A-F].</para>
    /// </summary>
    /// <param name="hexString">String to parse into bytes.</param>
    /// <param name="buffer">Buffer into which to store the decoded binary data.</param>
    /// <returns>The number of bytes decoded.</returns>
    private static int DecodeHexIntoBuffer(string hexString, byte[] buffer)
    {
        int count = 0;

        bool haveFirst = false;
        bool haveSecond = false;
        char first = '0';
        char second = '0';

        for (int i = 0; i < hexString.Length; i++)
        {
            if (!haveFirst)
            {
                first = hexString[i];
                haveFirst = char.IsLetterOrDigit(first);

                // we have to continue to the next iteration
                // or we will miss characters
                continue;
            }

            if (!haveSecond)
            {
                second = hexString[i];
                haveSecond = char.IsLetterOrDigit(second);
            }

            if (haveFirst && haveSecond)
            {
                string hex = "" + first + second;

                byte nextByte;
                if (byte.TryParse(hex, NumberStyles.HexNumber, null, out nextByte))
                {
                    // store the decoded byte into the next slot of the buffer
                    buffer[count++] = nextByte;
                }

                // reset the flags
                haveFirst = haveSecond = false;
            }
        }

        return count;
    }

1

缓慢但有趣的方式 :D

public static byte[] StringToByteArray(string hex)
{
    hex = hex.Replace(" ", "");
    hex = hex.Replace(":", "");
    return Enumerable.Range(0, hex.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
            .ToArray();
}

-jD


0

或许这个很可爱!

string source = "Hello World!";
 using (SHA256 sha256Hash = SHA256.Create())
 {
     //From String to byte array
     byte[] sourceBytes = Encoding.UTF8.GetBytes(source);
     byte[] hashBytes = sha256Hash.ComputeHash(sourceBytes);
     string hash = BitConverter.ToString(hashBytes).Replace("-", String.Empty);
     Console.WriteLine("The SHA256 hash of " + source + " is: " + hash);
 }

0

我在C#中使用这个,类似的代码在Java中。

    private static char[] hexdigit = "0123456789abcdef".ToCharArray();

    public static string hexlify(string argbuf) {
        int arglen = argbuf.Length;
        char[] argca = argbuf.ToCharArray ();
        StringBuilder retbuf = new StringBuilder(arglen * 2);
        for (int i = 0; i < arglen; i++) {
            char ch = argca[i];
            retbuf.Append(hexdigit[(ch >> 4) & 0xF]);
            retbuf.Append(hexdigit[ch & 0xF]);
        }
        return retbuf.ToString();
    }

    public static string unhexlify(string argbuf) {
        int arglen = argbuf.Length;
        if (arglen % 2 != 0) {
            throw new ArgumentOutOfRangeException ("Odd-length string");
        }
        char[] argca = argbuf.ToCharArray ();
        StringBuilder retbuf = new StringBuilder(arglen / 2);
        for (int i = 0; i < arglen; i += 2) {
            int top = Convert.ToInt32 (argca[i].ToString (), 16);
            int bot = Convert.ToInt32 (argca[i + 1].ToString (), 16);
            if (top == -1 || bot == -1) {
                throw new ArgumentOutOfRangeException ("Non-hexadecimal digit found");
            }
            retbuf.Append((char) ((top << 4) + bot));
        }
        return retbuf.ToString();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接