C# 和 Java 中的十六进制到字节数组给出不同的结果

Posted

技术标签:

【中文标题】C# 和 Java 中的十六进制到字节数组给出不同的结果【英文标题】:Hex to Byte Array in C# and Java Gives Different Results 【发布时间】:2013-05-31 03:20:18 【问题描述】:

首先,很抱歉这篇文章很长,我想把我的所有想法都包括在内,以便你们更容易发现我的代码有什么问题。

我想将十六进制字符串从 C# 应用程序传输到 Java 应用程序。但是,当我将相同的十六进制值转换为两种语言的字节数组时,输出是不同的。

例如,相同的十六进制值给出

  [101, 247, 11, 173, 46, 74, 56, 137, 185, 38, 40, 191, 204, 104, 83, 154]

在 C# 和

  [101, -9, 11, -83, 46, 74, 56, -119, -71, 38, 40, -65, -52, 104, 83, -102]

在 Java 中

以下是我在 C# 中使用的方法:

public static string ByteArrayToHexString(byte[] byteArray)

    return BitConverter.ToString(byteArray).Replace("-",""); //To convert the whole array


public static byte[] HexStringToByteArray(string hexString)


    byte[] HexAsBytes = new byte[hexString.Length / 2];
    for (int index = 0; index < HexAsBytes.Length; index++)
    
        string byteValue = hexString.Substring(index * 2, 2);
        HexAsBytes[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
    

    return HexAsBytes;

还有 Java 中的那些:

 public static String ByteArrayToHexString(byte[] bytes) 
     StringBuilder builder = new StringBuilder();
     for (byte b: bytes) 
     builder.append(String.format("%02x", b));
     
     return builder.toString().toUpperCase();
 

 public static byte[] HexStringToByteArray(String s) 
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) 
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    
    return data;

这是一个 C# 示例:

        String hexString = "65F70BAD2E4A3889B92628BFCC68539A";
        byte[] byteArray = HexBytes.HexStringToByteArray(hexString);
        //Using the debugger, byteArray = [101, 247, 11, 173, 46, 74, 56, 137, 185, 38, 40, 191, 204, 104, 83, 154]
        String hexString2 = HexBytes.ByteArrayToHexString(byteArray)
        Console.Write("HEX: " + hexString2);
        //Outputs 65F70BAD2E4A3889B92628BFCC68539A

还有一个 Java 示例:

    String hexString = "65F70BAD2E4A3889B92628BFCC68539A";
    byte[] byteArray = HexBytes.HexStringToByteArray(hexString);
    //Using the debugger, byteArray = [101, -9, 11, -83, 46, 74, 56, -119, -71, 38, 40, -65, -52, 104, 83, -102]
    String hexString2 = HexBytes.ByteArrayToHexString(byteArray);
    System.out.println("HEX: " + hexString2);
    //Outputs 65F70BAD2E4A3889B92628BFCC68539A

如您所见,当我进行相反的操作时,最终的 Hex 值等于第一个值,这意味着我的转换方式在两种语言中都可能很好。但我不明白为什么两种语言从十六进制到字节数组的转换是不同的。我认为十六进制只是另一个基数上的数字。

感谢您的帮助

赛德里克

更新

我通过用以下代码替换 C# 代码解决了这个问题:

public static string ByteArrayToHexString(sbyte[] byteArray)

    return BitConverter.ToString(convert(byteArray)).Replace("-", ""); //To convert the whole array


public static sbyte[] HexStringToByteArray(string hexString)


    byte[] HexAsBytes = new byte[hexString.Length / 2];
    for (int index = 0; index < HexAsBytes.Length; index++)
    
        string byteValue = hexString.Substring(index * 2, 2);
        HexAsBytes[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
    

    return convert(HexAsBytes);



private static sbyte[] convert(byte[] byteArray)

    sbyte[] sbyteArray = new sbyte[byteArray.Length];
    for (int i = 0; i < sbyteArray.Length; i++)
    
        sbyteArray[i] = unchecked((sbyte) byteArray[i]);
    

    return sbyteArray;


private static byte[] convert(sbyte[] sbyteArray)

    byte[] byteArray = new byte[sbyteArray.Length];
    for (int i = 0; i < byteArray.Length; i++)
    
        byteArray[i] = (byte) sbyteArray[i];
    
    return byteArray;

【问题讨论】:

附带说明,鲜为人知的类SoapHexBinary 是最简单的fastest 方法之一,可以将字符串转换为十六进制并返回您在示例中显示的格式。该类已在 .NET 框架 seance 1.0 中。 【参考方案1】:

但是,当我在两种语言上将相同的十六进制值转换为字节数组时,输出是不同的。

您所看到的只是字节在 Java 中是有符号的,而在 C# 中是无符号的。因此,如果您将 256 添加到 Java 中的任何负值,您将获得 C# 中显示的值。值中的实际 bits 是相同的 - 只是最高位是否被视为符号位的问题。

编辑:如 cmets 中所述,如果您曾经在调试器输出之外使用字节作为整数,您可以随时使用:

int someInt = someByte & 0xff;

获取无符号值。

【讨论】:

在 Java 中,不是将 256 添加到任何负字节值,而是可以得到一个 int 等于存储在任何字节值 b(b &amp; 0xff) 中的位的无符号值。 @TedHopp:确实。但是调试器输出没有帮助。不过会添加注释。 感谢您的提示。我用答案更新了我的问题。我在 C# 中从 byte[] 更改为 sbyte[]。【参考方案2】:

看起来这只是一个调试器问题。您在 Java 调试器中看到的那些负值是您在 C# 调试器中看到的无符号值的有符号等价物。例如,有符号字节 -9 == 无符号字节 247(注意它们总是相差 256)。数据很好。

【讨论】:

以上是关于C# 和 Java 中的十六进制到字节数组给出不同的结果的主要内容,如果未能解决你的问题,请参考以下文章

十六进制字符串到字节数组 C# [重复]

C# 校验并转换 16 进制字符串到字节数组

python中的十六进制字符串到字节数组

C#高级编程第三版--31.1.3上传文件

不同应用程序(WPF 和 Unity)中的 C# struct 序列化返回不同大小的字节数组

将字节数组从 c++ 传递到 c# 程序集都有哪些不同的方法?