Swift - 将UInt8字节转换为位数组

8
我正在尝试解码一个protobuf编码的消息,因此我需要将protobuf消息中的第一个字节(键)转换为位,以便找到字段号。如何将UInt8(字节)转换为位数组?
伪代码:
private func findFieldNum(from byte: UInt8) -> Int {
    //Byte is 0001 1010
    var fieldNumBits = byte[1] ++ byte[2] ++ byte[3] ++ byte[4] //concatentates bits to get 0011
    getFieldNum(from: fieldNumBits) //Converts 0011 to field number, 2^1 + 2^0 = 3
}

我看到了这个问题,它将一个位数组转换成字节数组。

5个回答

18

以下是从字节获取 Bit 数组的基本函数:

func bits(fromByte byte: UInt8) -> [Bit] {
    var byte = byte
    var bits = [Bit](repeating: .zero, count: 8)
    for i in 0..<8 {
        let currentBit = byte & 0x01
        if currentBit != 0 {
            bits[i] = .one
        }

        byte >>= 1
    }

    return bits
}

这里,Bit 是我定义的自定义枚举类型,定义如下:

enum Bit: UInt8, CustomStringConvertible {
    case zero, one

    var description: String {
        switch self {
        case .one:
            return "1"
        case .zero:
            return "0"
        }
    }
}

使用这个设置,以下代码的输出为:

let byte: UInt8 = 0x1f

print(bits(fromByte: byte))

将会是:

[1, 1, 1, 1, 1, 0, 0, 0]

你必须将字节写成十六进制,还是可以像00011010这样写成二进制? - Amanda
你可以使用 0b 后缀来写二进制。例如:0b10001111 - mohak
2
这个代码可以运行,但是返回的是原始位序列的反向(如二进制字面量0b11111000所表示的)。 - Nikolay Suvandzhiev
@NikolaySuvandzhiev bits() 返回的数组在第0个索引上有第0位,第1个索引上有第1位。这是有语义意义的,对吧?问题在于 print 函数将位数组视为普通列表,并从 0 索引开始打印其元素,而字面值通常是从第7位(MSb)开始编写的。 - mohak
反转顺序:Array(bits(fromByte: byte).reversed()) - SoftDesigner

3

mohak 的回答基础上进行改进。使用通用函数或扩展来适应不仅仅是 UInt8 的情况。

enum Bit: UInt8, CustomStringConvertible {
    case zero, one

    var description: String {
        switch self {
        case .one:
            return "1"
        case .zero:
            return "0"
        }
    }
}

func bits<T: FixedWidthInteger>(fromBytes bytes: T) -> [Bit] {
    // Make variable
    var bytes = bytes
    // Fill an array of bits with zeros to the fixed width integer length
    var bits = [Bit](repeating: .zero, count: T.bitWidth)
    // Run through each bit (LSB first)
    for i in 0..<T.bitWidth {
        let currentBit = bytes & 0x01
        if currentBit != 0 {
            bits[i] = .one
        }

        bytes >>= 1
    }

    return bits
}

extension FixedWidthInteger {
    var bits: [Bit] {
        // Make variable
        var bytes = self
        // Fill an array of bits with zeros to the fixed width integer length
        var bits = [Bit](repeating: .zero, count: self.bitWidth)
        // Run through each bit (LSB first)
        for i in 0..<self.bitWidth {
            let currentBit = bytes & 0x01
            if currentBit != 0 {
                bits[i] = .one
            }

            bytes >>= 1
        }

        return bits
    }
}

0

您可以进行一些位运算,以获取索引1-4处的位的值。

// An example byte
let a: UInt8 = 0b00011010

// Extract the bits (using 0b01111000 as a bitmask) 
// in indices 1 to 4, then shift right by 3 places
// to remove indices 5 to 7
var b = (a & 0b01111000) >> 3 

// In this case, b is now 3 (or 11 in binary)
print(b)

0

对我来说,Swift 4.5 运行良好:

func bits(fromByte byte: UInt8) -> [Bool] {
  var byte = byte
  var bits = [Bool](repeating: false, count: 8)
  for i in 0..<8 {
      let currentBit = byte & 0x01
      if currentBit != 0 {
          bits[i] = true
      }
      byte >>= 1
  }
  return bits
}

0

一个更短的:

enum Bit { case zero, one }

func bit(_ i: Int, of uint8: UInt8) -> Bit {
  let first8PowersOf2 = (0...7).map { return UInt8(1) << $0 }
  return (uint8 & first8PowersOf2[i] != 0) ? Bit.one : Bit.zero
}

func bitsFrom(_ uint8: UInt8) -> [Bit] {
  return Array((0...7)).reversed().map { bit($0, of: uint8) }
}

我们可以这样使用它:
let fromUInt8  = bitsFrom(42)
let fromBinary = bitsFrom(0b01011010)
let fromHexa   = bitsFrom(0xFF)

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