如何在JavaScript中交换变量的字节序(端序)

26

我正在接收和发送两个小端数字的十进制表示。 我想要:

  • 将一个变量左移8位
  • 对它们进行按位或运算
  • 将一个变量向左移动指定位数
  • 创建表示16位数字的前半部分和后半部分的2个8位数字。

JavaScript(根据https://developer.mozilla.org/en/JavaScript/Reference/Operators/Bitwise_Operators)在移位时使用大端表示......

对于我来说,字节序有点陌生(我只有90%的把握我的步骤是我想要的)。 所以交换有点令人眼花缭乱。 请帮帮我!我只需要知道如何以有效的方式交换顺序。(我只能想到使用toString()返回值上的for循环)

4个回答

44
function swap16(val) {
    return ((val & 0xFF) << 8)
           | ((val >> 8) & 0xFF);
}

解释:

  1. 假设val的值为0xAABB
  2. 使用0xFF进行&运算,得到最低位(LSB):0xBB
  3. 向左移动该结果8位:结果为0xBB00
  4. 向右移动val 8位:结果为0xAA(LSB已从右侧“掉落”)。
  5. 使用0xFF进行&运算,得到该结果的LSB:0xAA
  6. 通过|运算将步骤3和步骤5的结果组合在一起:
    0xBB00 | 0xAA 等于 0xBBAA

function swap32(val) {
    return ((val & 0xFF) << 24)
           | ((val & 0xFF00) << 8)
           | ((val >> 8) & 0xFF00)
           | ((val >> 24) & 0xFF);
}

解释:

  1. 假设val的值为0xAABBCCDD
  2. 通过将val0xFF进行&运算,掩码取得LSB:结果为0xDD
  3. 该结果向左移动24位:结果为0xDD000000
  4. 通过将val0xFF00进行&运算,掩码取得第二个字节:结果为0xCC00
  5. 该结果向左移动8位:结果为0xCC0000
  6. val向右移动8位:结果为0xAABBCC(LSB从右侧“掉落”)。
  7. 通过将该结果与0xFF00进行&运算,掩码取得第二个字节:结果为0xBB00
  8. val向右移动24位:结果为0xAA(除了MSB以外的所有位都从右侧“掉落”)。
  9. 通过将该结果与0xFF进行&运算,掩码取得LSB:结果为0xAA
  10. 通过将步骤3、5、7和9的结果|运算在一起:
    0xDD000000 | 0xCC0000 | 0xBB00 | 0xAA得到0xDDCCBBAA

1
所以,这似乎可以工作...有没有简单明了的解释,还是我应该直接拿去用? - griotspeak
1
@griotspeak:我已经编辑过来试图解释,虽然我怀疑你最好去阅读一下维基百科上有关字节序位运算的文章。 - LukeH
好的,这实际上不是我想要的,但我觉得我表达不清楚。我需要对前8(或16?)位进行文字反转。我认为这个答案已经让我接近解决问题了。 - griotspeak
1
function swap8(val) { return ((val & 0x1) << 7) | ((val & 0x2) << 5) | ((val & 0x4) << 3) | ((val & 0x8) << 1) | ((val >> 1) & 0x8) | ((val >> 3) & 0x4) | ((val >> 5) & 0x2) | ((val >> 7) & 0x1); } 是我的最终解决方案 - griotspeak
如果有人需要帮助,仅使用swap16()函数无法正确处理有符号整数。我不得不将其与此链接中的代码结合起来才能使一切正常工作:((((val & 0xFF) << 8) | ((val >> 8) & 0xFF)) << 16) >> 16 - martinez314
1
@whiskeyspider 已经提到了符号问题。这是我最终得出的结果:function swap16(val) { var x = ((val & 0xFF) << 8) | ((val >> 8) & 0xFF); if (x < 0) x = (x & 0x7FFF) + 0x8000; return x; } function swap32(val) { var x = ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF); if (x < 0) x = (x & 0x7FFFFFFF) + 0x80000000; return x; } - sarme

9
这个函数可以用于在JS中改变字节序:
const changeEndianness = (string) => {
        const result = [];
        let len = string.length - 2;
        while (len >= 0) {
          result.push(string.substr(len, 2));
          len -= 2;
        }
        return result.join('');
}

changeEndianness('AA00FF1234'); /// '3412FF00AA'

使用字符串做得很棒! - Joe Thomas

1

使用位移运算符 <<。例如:1 << 2 == 4。

我真的认为JavaScript的底层实现会使用它正在运行的平台使用的任何字节序。由于在JavaScript中无法直接访问内存,因此您永远不必担心数字在内存中的物理表示方式。无论字节顺序如何,位移整数值始终产生相同的结果。只有在使用指针查看内存中的单个字节时才会看到差异。


我现在手上有一个小端模式的十进制数表示,需要将它转换为另一种小端模式的十进制数表示。由于这个数值还需要用于驱动二进制显示器,因此转换过程非常重要。 - griotspeak
我认为我明白你的意思,并且在许多情况下,你是正确的。我得到和给出的数字是为了从左到右打开8个灯,因此,我将基本上查看单个字节。 - griotspeak

0
以下是一个用于大端和小端(以及反之)数组交换的一行代码。交换是在字节级别上使用 reverse 函数完成的。我猜对于大型数组来说,它比循环标量交换函数更有效率。

function swapbyte(x) {
    return new Float64Array(new Int8Array(x.buffer).reverse().buffer).reverse()
}

// Example
buf = new ArrayBuffer(16);        // for 2 float64 numbers
enBig = new Float64Array(buf);
enBig[0] = 3.2073756306779606e-192;
enBig[1] = 2.7604354232023903e+199;

enLittle = swapbyte(enBig)

// two famous numbers are revealed
console.log(enLittle)
// Float64Array [ 6.283185307179586, 2.718281828459045 ]

// swapping again yields the original input
console.log(swapbyte(enLittle))
// Float64Array [ 3.2073756306779606e-192, 2.7604354232023903e+199 ]


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