将小端字节序转换为大端字节序

22

大家好,

我一直在网上练习编码问题。目前我正在解决一个问题陈述Problems,其中我们需要将大端序与小端序进行转换。但是,考虑到给定的示例,我无法记录下具体步骤:

123456789 converts to 365779719

我考虑的逻辑是:
1 > 获取整数值(由于我使用的是Windows x86,输入为小端)
2 > 生成相同的十六进制表示。
3 > 反转表示并生成大端整数值

但我显然漏掉了什么。

请问有谁能指导我吗?我正在使用Java 1.5编码。

7个回答

40

由于编写软件的重要部分是重复使用现有的解决方案,因此首先应该查阅您所使用语言/库的文档。

reverse = Integer.reverseBytes(x);

我不知道这个函数的效率如何,但是对于切换大量数字来说,ByteBuffer 应该提供不错的性能。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

...

int[] myArray = aFountOfIntegers();
ByteBuffer buffer = ByteBuffer.allocate(myArray.length*Integer.BYTES);

buffer.order(ByteOrder.LITTLE_ENDIAN);
for (int x:myArray) buffer.putInt(x);

buffer.order(ByteOrder.BIG_ENDIAN);
buffer.rewind();
int i=0;
for (int x:myArray) myArray[i++] = buffer.getInt(x);

正如评论中eversor所指出的那样,ByteBuffer.putInt()是一个可选方法,并且可能在所有Java实现中都不可用。

自己动手的方法

Stacker的回答很不错,但是还有改进的空间。

   reversed = (i&0xff)<<24 | (i&0xff00)<<8 | (i&0xff0000)>>8 | (i>>24)&0xff;

通过调整位掩码,我们可以摆脱括号。例如,(a & 0xFF)<<8 等同于 a<<8 & 0xFF00。右边的括号本来就不必要。

   reversed = i<<24 & 0xff000000 | i<<8 & 0xff0000 | i>>8 & 0xff00 | i>>24 & 0xff;

由于左移操作会将零位移入,所以第一个掩码是多余的。我们可以使用逻辑移位运算符来除去最右边的掩码,它只移入零位。

   reversed = i<<24 | i>>8 & 0xff00 | i<<8 & 0xff0000 | i>>>24;

运算符优先级,在这里可以找到详细的位移运算符信息,请查阅Java语言规范


1
请注意,ByteBuffer.putInt()是一个可选方法。在某些系统中可能会遇到问题。 - eversor
1
库函数reverseBytes的定义非常相似: return ((i >>> 24) ) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | ((i << 24)); - BullyWiiPlaza
聪明人想得一样。 ;) 更有理由使用库函数,以防编译器知道更好的实现方式。例如,某些架构上可能会提供专用硬件。 - Wolfram Schmied
如果可以的话,我会点赞一千次......互联网上有太多人试图重复发明轮子,而我花费了太多时间查看虚假答案,这些答案比必要的复杂得多。 - tjwrona1992

26

看这个

int little2big(int i) {
    return (i&0xff)<<24 | (i&0xff00)<<8 | (i&0xff0000)>>8 | (i>>24)&0xff;
}

4
也许更清晰的写法:return ((i << 24) + ((i << 8) & 0x00FF0000) + ((i >> 8) & 0x0000FF00) + (i >>> 24)) - Lawrence Dol
2
另外,这将切换小端到大端以及大端到小端,因此方法名称不够广泛。也许应该叫作swapEndian? - Lawrence Dol
5
我会使用 | 替代 +,因为位或运算可能更快,并且更容易被编译器/运行时优化。 - Lawrence Dol
我把它称为“int swapInt(int)” - Raúl Salinas-Monteagudo

22
你需要明白的是字节序交换处理的是表示整数的字节。因此,4字节的数字27看起来像是0x0000001B。要将该数字转化为0x1B000000... 以你所举的例子,123456789的十六进制表示是0x075BCD15,需要转换为0x15CD5B07或十进制形式的365779719。
Stacker张贴的函数是通过位移操作移动这些字节;具体而言,语句i&0xffi中提取出最低位字节,然后<< 24将其向上移动24位,从位置1-8移到25-32。每个表达式都是按照这种方式处理的。
例如代码,请参考这个工具。

这更像是一个4字节的数字27... 8字节应该是:0x000000000000001B - Guido Tarsia

16

自JDK 1.5版起,Java原始类型包装类支持使用reverseBytes方法进行字节反转。

Short.reverseBytes(short i)
Integer.reverseBytes(int i)
Long.reverseBytes(long i)

这只是给那些在2018年寻找答案的人的一点贡献。


2
我认为这也可以帮助:
int littleToBig(int i)
{
    int b0,b1,b2,b3;

    b0 = (i&0x000000ff)>>0;
    b1 = (i&0x0000ff00)>>8;
    b2 = (i&0x00ff0000)>>16;
    b3 = (i&0xff000000)>>24;

    return ((b0<<24)|(b1<<16)|(b2<<8)|(b3<<0));
}

9
注意:这是不正确的!第四个任务应该是 b3 = (i & 0xff000000) >>> 24 来进行修正。否则,如果 i 的最高位为1,则会将其复制到返回结果的最高24位中。 - Rinke

0

只需在Java中使用Integer包装类下的静态函数(reverseBytes(int i))

Integer i=Integer.reverseBytes(123456789);
System.out.println(i);

输出:

365779719

-1

下面的方法可以将一个字节值中的位序反转:

public static byte reverseBitOrder(byte b) {
    int converted = 0x00;
    converted ^= (b & 0b1000_0000) >> 7;
    converted ^= (b & 0b0100_0000) >> 5;
    converted ^= (b & 0b0010_0000) >> 3;
    converted ^= (b & 0b0001_0000) >> 1;
    converted ^= (b & 0b0000_1000) << 1;
    converted ^= (b & 0b0000_0100) << 3;
    converted ^= (b & 0b0000_0010) << 5;
    converted ^= (b & 0b0000_0001) << 7;

    return (byte) (converted & 0xFF);
}

1
字节本身并没有大小端之分,除非在基于半字节的机器/软件上,比如古老的IBM主机。在这种情况下,需要将低4位和高4位交换,就像它们是一个高字和低字一样。 - Lisa

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