繁体   English   中英

Swift 位数组到字节数组(UInt8 数组)

[英]Swift Bit array to Bytes array (UInt8 array)

我有位数组:

var bits: [Bit]

以及如何将其转换为字节数组:

var bytes: [UInt8]

例如,我有 280 位,字节数组中应该有 35 个 UInt8。 我可以想到解决方案,我取 8 位并检查第一个是否为真,第二个是否为真等等,然后对结果求和并有价值。 我会对位数组中的每 8 位执行此操作。 但我认为这将是一个糟糕的解决方案(它可以工作,但需要进行不必要的计算)。 我认为可能会有更快的解决方案,但我在这方面真的很糟糕,所以我正在寻求帮助。 谢谢

一种可能的解决方案是枚举数组中的所有位,并为所有“一”位设置UInt8数组中的相应位:

func bitsToBytes(bits: [Bit]) -> [UInt8] {
    let numBits = bits.count
    let numBytes = (numBits + 7)/8
    var bytes = [UInt8](count : numBytes, repeatedValue : 0)

    for (index, bit) in enumerate(bits) {
        if bit == .One {
            bytes[index / 8] += 1 << (7 - index % 8)
        }
    }

    return bytes
}

主要思想是对于位数组中给定的indexindex / 8是字节数组中对应的索引, index % 8是字节中的位位置。 您可以使用index % 87 - index % 8作为移位量,具体取决于所需的位顺序。

例子:

// 0110 0100  0000 1001
let bits : [Bit] = [.Zero, .One, .One, .Zero,  .Zero, .One, .Zero, .Zero,  .Zero, .Zero, .Zero, .Zero,  .One, .Zero, .Zero, .One]
let bytes = bitsToBytes(bits)
println(bytes) // [100, 9]

或者,您可以“内联”每组 8 位的计算。 您必须检查哪种解决方案在您的情况下表现更好。

func bitsToBytes(bits: [Bit]) -> [UInt8] {
    let numBits = bits.count
    let numBytes = numBits/8
    var bytes = [UInt8](count : numBytes, repeatedValue : 0)
    for pos in 0 ..< numBytes {
        let val = 128 * bits[8 * pos].toIntMax() + 
            64 * bits[8 * pos + 1].toIntMax() +
            32 * bits[8 * pos + 2].toIntMax() +
            16 * bits[8 * pos + 3].toIntMax() +
            8 * bits[8 * pos + 4].toIntMax() +
            4 * bits[8 * pos + 5].toIntMax() +
            2 * bits[8 * pos + 6].toIntMax() +
            1 * bits[8 * pos + 7].toIntMax()
        bytes[pos] = UInt8(val)
    }
    return bytes
}

这里,为简单起见,如果位数不是 8 的倍数,则忽略任何多余的位。 相同的代码也可以写得更“Swiftier”为

func bitsToBytes(bits: [Bit]) -> [UInt8] {
    return map(0 ..< bits.count/8) {
        pos in
        let val = 128 * bits[8 * pos].toIntMax() + 
            64 * bits[8 * pos + 1].toIntMax() +
            32 * bits[8 * pos + 2].toIntMax() +
            16 * bits[8 * pos + 3].toIntMax() +
            8 * bits[8 * pos + 4].toIntMax() +
            4 * bits[8 * pos + 5].toIntMax() +
            2 * bits[8 * pos + 6].toIntMax() +
            1 * bits[8 * pos + 7].toIntMax()
        return (UInt8(val))
    }
}

基准:现在是一个快速而肮脏的基准测试应用程序(下面的代码),比较了各种解决方案。 它测量转换长度为 256 的 10,000 位数组的时间。测试是在 MacBook Pro 2,3 GHz Intel Core i7 上完成的,代码使用“Release”配置编译。

Swift 1.1/Xcode 6.2 (6C131e) 的结果:

Martin1: 0.0460730195045471
Martin2: 0.0280380249023438
Martin3: 0.0374950170516968
Antonio: 5.85363000631332
Nate   : 4.86936402320862

Swift 1.2/Xcode 6.3 (6D532l) 的结果:

Martin1: 0.0228430032730103
Martin2: 0.00573796033859253
Martin3: 0.00732702016830444
Antonio: 0.515677988529205
Nate   : 0.634827971458435

代码:

protocol BitsToBytesConverter {
    var ident : String { get }
    func bitsToBytes(bits: [Bit]) -> [UInt8]
}

class MR1 : BitsToBytesConverter {

    let ident = "Martin1"
    func bitsToBytes(bits: [Bit]) -> [UInt8] {
        let numBits = bits.count
        let numBytes = (numBits + 7)/8
        var bytes = [UInt8](count : numBytes, repeatedValue : 0)

        for (index, bit) in enumerate(bits) {
            if bit == .One {
                bytes[index / 8] += UInt8(1 << (7 - index % 8))
            }
        }

        return bytes
    }
}

class MR2 : BitsToBytesConverter {

    let ident = "Martin2"

    func bitsToBytes(bits: [Bit]) -> [UInt8] {
        let numBits = bits.count
        let numBytes = numBits/8
        var bytes = [UInt8](count : numBytes, repeatedValue : 0)
        for pos in 0 ..< numBytes {
            let val = 128 * bits[8 * pos].toIntMax() + 
                64 * bits[8 * pos + 1].toIntMax() +
                32 * bits[8 * pos + 2].toIntMax() +
                16 * bits[8 * pos + 3].toIntMax() +
                8 * bits[8 * pos + 4].toIntMax() +
                4 * bits[8 * pos + 5].toIntMax() +
                2 * bits[8 * pos + 6].toIntMax() +
                1 * bits[8 * pos + 7].toIntMax()
            bytes[pos] = UInt8(val)
        }
        return bytes
    }
}

class MR3 : BitsToBytesConverter {

    let ident = "Martin3"

    func bitsToBytes(bits: [Bit]) -> [UInt8] {
        return map(0 ..< bits.count/8) {
            pos in
            let val = 128 * bits[8 * pos].toIntMax() + 
                64 * bits[8 * pos + 1].toIntMax() +
                32 * bits[8 * pos + 2].toIntMax() +
                16 * bits[8 * pos + 3].toIntMax() +
                8 * bits[8 * pos + 4].toIntMax() +
                4 * bits[8 * pos + 5].toIntMax() +
                2 * bits[8 * pos + 6].toIntMax() +
                1 * bits[8 * pos + 7].toIntMax()
            return (UInt8(val))
        }
    }
}

class AB : BitsToBytesConverter {

    let ident = "Antonio"

    typealias IntegerType = UInt8

    func bitsToBytes(bits: [Bit]) -> [UInt8] {

        let initial = [IntegerType]()

        return reduce(enumerate(bits), initial) { array, element in
            // The size in bits of a UInt8
            let size = sizeof(IntegerType) * 8

            // Create a mutable copy of the array returned at the previous iteration
            var next = array

            // If it's the first iteration, or an iteration divisible by the size of UInt8, 
            // append a new element to the array
            if element.index % size == 0 {
                next.append(0x00)
            }

            // Shift all bits of the last element to the left
            next[next.count - 1] <<= 1

            // If the current bit is one, add 1 to the rightmost bit
            // Using a logical OR
            if element.element == .One {
                next[next.count - 1] |= 0x01
            }

            return next
        }
    }
}

class NC : BitsToBytesConverter {

    let ident = "Nate   "

    func group<T>(array: [T], byCount groupCount: Int) -> [Slice<T>] {
        // get a list of the start indices
        let startIndices = stride(from: 0, to: array.count, by: groupCount)
        // add `groupCount` to each to get the end indices
        let endIndices = lazy(startIndices).map { advance($0, groupCount, array.count) }

        // zip those together & map onto an array of slices of the input array
        return map(Zip2(startIndices, endIndices)) {
            array[$0.0 ..< $0.1]
        }
    }

    func bitsToByte(bits: Slice<Bit>) -> UInt8 {
        return bits.reduce(0) { accumulated, current in
            accumulated << 1 | (current == .One ? 1 : 0)
        }
    }

    func bitsToBytes(bits: [Bit]) -> [UInt8] {
        return group(bits, byCount: 8).map(bitsToByte)
    }
}


let numBits = 256 // Bits per bit array
let numBitArrays = 10000 // Number of bit arrays

func randomBits() -> [Bit] {
    return map(0 ..< numBits) { _  in
        Bit(rawValue: Int(arc4random_uniform(2)))!
    }
}

func randomBitsArray() -> [[Bit]] {
    return map(0 ..< numBitArrays) { _ in
        randomBits()
    }
}

let bitsArray = randomBitsArray()

func test(conv : BitsToBytesConverter) {
    let x = conv.bitsToBytes([])
    let startTime = NSDate()
    for bits in bitsArray {
        let bytes = conv.bitsToBytes(bits)
    }
    let duration = -startTime.timeIntervalSinceNow
    println("\(conv.ident): \(duration)")
}

test(MR1())
test(MR2())
test(MR3())
test(AB())
test(NC())

这是一个有趣的问题。 我将此视为两个较小的问题:(1)如何将一个Bit数组拆分为一个Bit数组数组,其中每个较小的数组是一个字节的位,以及(2)如何将这些较小的数组转换为一个字节每个。

为了解决第一个问题,我们可以编写一个函数,将数组分组为特定大小的切片:

func group<T>(array: [T], byCount groupCount: Int) -> [Slice<T>] {
    // get a list of the start indices
    let startIndices = stride(from: 0, to: s.count, by: groupCount)
    // add `groupCount` to each to get the end indices
    let endIndices = lazy(startIndices).map { advance($0, groupCount, array.count) }

    // zip those together & map onto an array of slices of the input array
    return map(zip(startIndices, endIndices)) {
        array[$0.0 ..< $0.1]
    }
}

为了解决第二个问题,我们可以编写一个函数,将group(_:byCount:)返回的每个Slice<Bit>转换为UInt8 在每一步,它将值左移一位,然后如果该元素是.One则设置位:

func bitsToByte(bits: Slice<Bit>) -> UInt8 {
    return bits.reduce(0) { accumulated, current in
        accumulated << 1 | (current == .One ? 1 : 0)
    }
}

最后,您可以依次调用它们中的每一个,或将它们组合起来以获得您的结果:

// 1111 1111  1000 0000  0000 0001  0101 0101
let bits : [Bit] = [.One, .One, .One, .One, .One, .One, .One, .One,
    .One, .Zero,  .Zero, .Zero, .Zero, .Zero, .Zero, .Zero,
    .Zero,  .Zero, .Zero, .Zero, .Zero, .Zero, .Zero, .One,
    .Zero, .One, .Zero, .One, .Zero, .One, .Zero, .One]
let bytes = group(bits, byCount: 8).map(bitsToByte)
// [255, 128, 1, 85]

如果您更喜欢函数式方法,但计算成本更高,那么您可以将reduceenumerate结合使用。

后者,给定一个元素序列,创建一个(index, element)元组序列。 我们需要索引来知道位的位置。

reduce用于将Bit数组UInt8UInt8数组

typealias IntegerType = UInt8

let initial = [IntegerType]()

let result = reduce(enumerate(bits), initial) { array, element in
    // The size in bits of a UInt8
    let size = sizeof(IntegerType) * 8

    // Create a mutable copy of the array returned at the previous iteration
    var next = array

    // If it's the first iteration, or an iteration divisible by the size of UInt8, 
    // append a new element to the array
    if element.index % size == 0 {
        next.append(0x00)
    }

    // Shift all bits of the last element to the left
    next[next.count - 1] <<= 1

    // If the current bit is one, add 1 to the rightmost bit
    // Using a logical OR
    if element.element == .One {
        next[next.count - 1] |= 0x01
    }

    return next
}

返回的结果是一个UInt8数组。

更新忘记提了,如果要转换为不同的整数类型,只需更改IntegerType别名。

这里有一些很酷的代码 @martin-r @antonio @nate-cook,但我在将此代码转换为Swift 3供我使用时似乎也发现了一些正确性问题。 这个修改后的片段的特点:

  • 在 Playground 中运行的 Xcode 8、Swift 3.0
  • 包括验证功能(注释掉)
  • 有 Bit 枚举来表示 Bit 类型。 (位类型已从 Swift 3 中删除)
  • 虽然这仍然有计时工具,但我还没有尝试真正测试不同方法的速度。 看来我们首先需要确保正确的结果。
  • 只有 Martin1 和 Martin2 编码。 其他方法留给读者练习;)。
Martin1: 0.112455010414124: hash 0
Martin2: 1.06640499830246: hash 0

无论如何,这是我的代码强烈基于其他人的工作。 希望有些人觉得这很有用。

import Foundation

enum Bit { case zero, one
    func asInt() -> Int {
        return (self == .one) ? 1 : 0
    }
}

protocol BitsToBytesConverter {
    var ident : String { get }
    func bitsToBytes(bits: [Bit]) -> [UInt8]
}

class MR1 : BitsToBytesConverter {

    let ident = "Martin1"

    func bitsToBytes(bits: [Bit]) -> [UInt8] {
        assert(bits.count % 8 == 0, "Bit array size must be multiple of 8")

        let numBytes = 1 + (bits.count - 1) / 8
        var bytes = [UInt8](repeating: 0, count: numBytes)

        for (index, bit) in bits.enumerated() {
            if bit == .one {
                bytes[index / 8] += UInt8(1 << (7 - index % 8))
            }
        }
        return bytes
    }
}

class MR2 : BitsToBytesConverter {

    let ident = "Martin2"

    func bitsToBytes(bits: [Bit]) -> [UInt8] {
        assert(bits.count % 8 == 0, "Bit array size must be multiple of 8")

        let numBytes = 1 + (bits.count - 1) / 8

        var bytes = [UInt8](repeating : 0, count : numBytes)
        for pos in 0 ..< numBytes {
            let val = 128 * bits[8 * pos].asInt() +
                64 * bits[8 * pos + 1].asInt() +
                32 * bits[8 * pos + 2].asInt() +
                16 * bits[8 * pos + 3].asInt() +
                8 * bits[8 * pos + 4].asInt() +
                4 * bits[8 * pos + 5].asInt() +
                2 * bits[8 * pos + 6].asInt() +
                1 * bits[8 * pos + 7].asInt()
            bytes[pos] = UInt8(val)
        }
        return bytes
    }
}

func format(bits: [Bit]) -> String {
    return bits.reduce("") { (current, next) in
        return current.appending((next == .one) ? "1" : "0")
    }
}
func randomBits(ofLength: Int) -> [Bit] {
    return (0 ..< ofLength).map() { _  in
        Int(arc4random_uniform(2)) == 1 ? .one : .zero
    }
}
func isValid(conv: BitsToBytesConverter, input: [Bit], expected: [UInt8]) -> Bool {
    let actual = conv.bitsToBytes(bits: input)
    var bIsValid = (actual.count == expected.count)
    if bIsValid {
        for index in 0 ..< expected.count {
            if actual[index] != expected[index] {
                bIsValid = false
                break
            }
        }
    }
    return bIsValid
}
func validateAll() {
    let input0: [Bit] = [.one, .zero, .one, .zero, .one, .zero, .one, .zero]
    let expected0: [UInt8] = [170]

    let input1: [Bit] = (0..<8).map { _ in .zero }
    let expected1: [UInt8] = [0]

    let input2: [Bit] = (0..<8).map { _ in .one }
    let expected2: [UInt8] = [255]

    let input3 = input1 + input2
    let expected3 = expected1 + expected2

    let input4 = input2 + input1
    let expected4 = expected2 + expected1

    let inputs = [input0, input1, input2, input3, input4]
    let expecteds = [expected0, expected1, expected2, expected3, expected4]

    let convs: [BitsToBytesConverter] = [MR1(), MR2()]

    for inIndex in 0 ..< inputs.count {
        let input = inputs[inIndex]
        let expected = expecteds[inIndex]

        let formattedBits = format(bits: input)
        print("Checking: \(formattedBits) -> \(expected)")

        for conv in convs {
            let bIsValid = isValid(conv: conv, input: input, expected: expected)
            print("\(conv.ident) valid = \(bIsValid)")
        }
    }
}
func timeTest(conv : BitsToBytesConverter, bitsArray: [[Bit]]) {
    // Prime compilation, caching, ...
    let _ = conv.bitsToBytes(bits: bitsArray[0])

    let startTime = NSDate()
    var hash = 0

    for bits in bitsArray {
        let _ = conv.bitsToBytes(bits: bits)

        // Hash to compare results across methods
        //let result = conv.bitsToBytes(bits: bits)
        //let bString = format(bits: bits)
        //print("Bits: \(bString): Bytes: \(result)")
        //hash = result.reduce(0) { (previous, next) in
        //    return previous + next.hashValue
        //}
    }
    let duration = -startTime.timeIntervalSinceNow
    print("\(conv.ident): \(duration): hash \(hash)")
}
func testAll() {
    let numBits = 128 // Bits per bit array
    let numBitArrays = 100 // Number of bit arrays

    let testValues = (0 ..< numBitArrays).map() { _ in
        randomBits(ofLength: numBits)
    }
    timeTest(conv: MR1(), bitsArray: testValues)
    timeTest(conv: MR2(), bitsArray: testValues)
}
//validateAll()
testAll()

快速 5

func convertBitsToBytes(bits : [Bit]) -> [UInt8] {
    let numBits = bits.count
    let numBytes = (numBits + 7)/8
    var bytes = [UInt8](unsafeUninitializedCapacity: numBytes) { (buffer, initializedCount) in
        
    }
    
    for (index, bit) in bits.enumerated() {
        if bit == .one {
            bytes[index / 8] += UInt8(1 << (7 - index % 8))
        }
    }
    return bytes
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM