Java将整数转换为十六进制,然后再转回整数

90

我有下面的代码...

int Val=-32768;
String Hex=Integer.toHexString(Val);

这相当于ffff8000

int FirstAttempt=Integer.parseInt(Hex,16); // Error "Invalid Int"
int SecondAttempt=Integer.decode("0x"+Hex);  // Error "Invalid Int"

初始时,它将值-32768转换为十六进制字符串ffff8000,但无法将十六进制字符串转换回整数。

在.Net中,它按预期工作,并返回-32768。

我知道我可以编写自己的方法来手动转换,但我想知道是否有什么我错过了,或者这确实是一个漏洞?


1
可能是Java负整数转十六进制及其逆过程失败的重复问题。 - Andreas Dolk
6
提示:按照惯例,变量名以小写字母开头:int firstAttempt = 5; - Simulant
10个回答

83
int val = -32768;
String hex = Integer.toHexString(val);

int parsedResult = (int) Long.parseLong(hex, 16);
System.out.println(parsedResult);

这就是你可以做到的方法。

为什么你的方法不起作用:因为Integer.parseInt只接受有符号整数,而toHexString生成无符号结果。因此,如果你插入一个大于0x7FFFFFF的值,就会自动抛出错误。如果你解析成long,它仍然是有符号的。但当你将它强制转换回int时,它会溢出到正确的值。


53

因为数字是负数,所以它溢出了。

尝试这样做,它将可以工作:

int n = (int) Long.parseLong("ffff8000", 16);

谢谢Roni,那似乎是最好的解决方法。虽然Int.parseInt仍然不能按照我的期望工作,这似乎有点奇怪。 - Rich S
ffff8000不适合int(大于最大int),这是一个正数(它是一个字符串,所以只有带负号才是负数) - roni bar yanai
1
这是因为 parseInt 接受有符号整数,而 toHexString 生成无符号结果(请参见我的答案)... - brimborium
谢谢,你救了我的一天 :) - Vineesh TP
1
@roni,如果十六进制是字符串值,例如String Hex=Integer.toHexString("xyz");,如何从十六进制中获取字符串"xyz"? - subodh

30
  • int 转十六进制:

    Integer.toHexString(intValue);
    
  • 十六进制转 int

    Integer.valueOf(hexString, 16).intValue();
    

如果值不在int的范围内,您也可以考虑使用long

  • 十六进制转long

    Long.valueOf(hexString, 16).longValue()
    
  • long转十六进制:

    Long.toHexString(longValue)
    

11
值得一提的是,Java 8中有两种方法 Integer.parseUnsignedInt Long.parseUnsignedLong ,可以实现您想要的功能,具体来说: Integer.parseUnsignedInt("ffff8000",16) == -32768 名称有点令人困惑,因为它从十六进制字符串解析出一个带符号的整数,但它可以完成工作。

7

尝试使用BigInteger类,它可以解决问题。

int Val=-32768;
String Hex=Integer.toHexString(Val);

//int FirstAttempt=Integer.parseInt(Hex,16); // Error "Invalid Int"
//int SecondAttempt=Integer.decode("0x"+Hex);  // Error "Invalid Int"
BigInteger i = new BigInteger(Hex,16);
System.out.println(i.intValue());

6

如果您尝试转换UTF-16解码字符等有符号字节时,Integer.toHexString(byte / integer)无法正常工作,您需要使用以下方法:

Integer.toString(byte/integer, 16);

或者

String.format("%02X", byte/integer);

你可以使用反转

Integer.parseInt(hexString, 16);

3

Java的parseInt方法实际上是一堆吃掉"false"十六进制的代码:如果你想将-32768转换为十进制,你应该先将其绝对值转换为十六进制,然后在字符串前加上'-'。

以下是Integer.java文件的示例:

public static int parseInt(String s, int radix)

描述非常明确:
* Parses the string argument as a signed integer in the radix 
* specified by the second argument. The characters in the string 
...
...
* parseInt("0", 10) returns 0
* parseInt("473", 10) returns 473
* parseInt("-0", 10) returns 0
* parseInt("-FF", 16) returns -255

2
以下代码可以正常工作:

最初的回答:

int a=-32768;
String a1=Integer.toHexString(a);
int parsedResult=(int)Long.parseLong(a1,16);
System.out.println("Parsed Value is " +parsedResult);

2
使用 Integer.toHexString(...) 是一个很好的答案。但我个人更喜欢使用 String.format(...)
尝试使用以下示例进行测试。
byte[] values = new byte[64];
Arrays.fill(values, (byte)8);  //Fills array with 8 just for test
String valuesStr = "";
for(int i = 0; i < values.length; i++)
    valuesStr += String.format("0x%02x", values[i] & 0xff) + " ";
valuesStr.trim();

1

呵呵,好奇。我认为这是一种“有意的漏洞”,可以这么说。

根本原因在于Integer类的编写方式。基本上,parseInt被“优化”用于正数。当它解析字符串时,它会累加地构建结果,但是取反。然后它翻转最终结果的符号。

例如:

66 = 0x42

像这样解析:

4*(-1) = -4
-4 * 16 = -64 (hex 4 parsed)

-64 - 2 = -66 (hex 2 parsed)

return -66 * (-1) = 66

现在,让我们来看看你的例子 FFFF8000

16*(-1) = -16 (first F parsed)
-16*16 = -256 

-256 - 16 = -272 (second F parsed)
-272 * 16 = -4352 

-4352 - 16 = -4368 (third F parsed)
-4352 * 16 = -69888

-69888 - 16 = -69904 (forth F parsed)
-69904 * 16 = -1118464 

-1118464 - 8 = -1118472 (8 parsed)
-1118464 * 16 = -17895552 

-17895552 - 0 = -17895552 (first 0 parsed)
Here it blows up since -17895552 < -Integer.MAX_VALUE / 16 (-134217728). 
Attempting to execute the next logical step in the chain (-17895552 * 16)
would cause an integer overflow error.

编辑(补充):为了使parseInt()在-Integer.MAX_VALUE <= n <= Integer.MAX_VALUE的范围内“一致”工作,他们必须实现逻辑来在累积结果达到-Integer.MAX_VALUE时“旋转”,从整数范围的最大端重新开始,并从那里向下继续。为什么他们没有这样做,我们不得不问Josh Bloch或者是第一次实现它的人。这可能只是一种优化。

然而,

Hex=Integer.toHexString(Integer.MAX_VALUE);
System.out.println(Hex);
System.out.println(Integer.parseInt(Hex.toUpperCase(), 16));

这个很好地工作,正是因为这个原因。在 Integer 的源代码中,你可以找到这个注释。

// Accumulating negatively avoids surprises near MAX_VALUE

2
// 累加负数可以避免在最大值附近出现意外情况,但它会在小于0的时候引入意外情况 ^^ - brimborium

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