【问题标题】:Converting NSData to Integer in Swift在 Swift 中将 NSData 转换为整数
【发布时间】:2014-12-01 09:21:28
【问题描述】:

在 Objective-C 中,代码看起来像这样,并且运行完美,

NSInteger random = arc4random_uniform(99) + 1 
NSData *data = [NSData dataWithBytes:& random length: sizeof(random)];
int value = *(int*)([data bytes]);

如何在 Swift 中做到这一点?

【问题讨论】:

    标签: swift integer nsdata


    【解决方案1】:

    像这样:

    var src: NSInteger = 2525
    var out: NSInteger = 0
    
    let data = NSData(bytes: &src, length: sizeof(NSInteger))
    data.getBytes(&out, length: sizeof(NSInteger))
    println(out) // ==> 2525
    

    【讨论】:

    • 绝对完美!谢谢!
    • sizeofValue(random)
    • 在 Swift3 中:sizeof(NSInteger) 变为 MemoryLayout.size 并且 println 变为 print。
    • 请注意,在 Swift 3 或更高版本的sizeofValue 中直接从类型而不是对象获取大小是MemoryLayout.size(ofValue: value)
    【解决方案2】:

    在 Swift 4 中:

    Data 流中提取整数值时需要考虑几件事情。 签名Endianess。所以我在Data 的扩展中提出了一个函数,它从您要提取的整数类型中推断出 Signedness 并将 EndianessIndex 作为参数传递。可提取的整数类型均符合FixedWidthInteger协议。

    提醒:此函数不会检查Index 范围是否在Data 缓冲区的范围内,因此它可能会崩溃,具体取决于所提取类型的大小相对于缓冲区结束。

    extension Data {
        enum Endianness {
            case BigEndian
            case LittleEndian
        }
        func scanValue<T: FixedWidthInteger>(at index: Data.Index, endianess: Endianness) -> T {
            let number: T = self.subdata(in: index..<index + MemoryLayout<T>.size).withUnsafeBytes({ $0.pointee })
            switch endianess {
            case .BigEndian:
                return number.bigEndian
            case .LittleEndian:
                return number.littleEndian
            }
        }
    }
    

    示例:

    let data = Data(bytes: [0xFF,0x1F,0x1F,0xFF])
    
    let number1 = data.scanValue(at: 0, endianess: .LittleEndian) as UInt16
    let number2 = data.scanValue(at: 0, endianess: .BigEndian) as UInt16
    
    let number3: Int16 = data.scanValue(at: 2, endianess: .LittleEndian)
    let number4: Int16 = data.scanValue(at: 2, endianess: .BigEndian)
    

    结果:

    number1 is 8191
    number2 is 65311
    number3 is -225
    number4 is 8191

    观察函数调用以了解如何推断要提取的类型。当然 Endianess 对于 Int8UInt8 没有意义,但该函数按预期工作。

    如果需要,稍后可以将值转换为 Int

    【讨论】:

    • 嗨 - 你有一个更简单的解决方案来将数据 转换为 UInt8 2 或 Int 2 吗?
    • @richc:获得UInt8 的最简单方法是如果您确切知道所需字节的索引,则只需使用下标:data[index]
    • Data.IndexData 扩展中是多余的。只使用Index
    • 我认为你应该使用MemoryLayout.stride - 而不是size。请参阅stride 的文档,将其定义为“存储在连续内存或 Array 中时从 T 的一个实例开始到下一个实例开始的字节数”和“字节数当 UnsafePointer 实例增加时移动"
    【解决方案3】:

    对于 Swift 3,您可以这样做(小端,但大端类似):

    func getInt(fromData data: Data, start: Int) -> Int32 {
      let intBits = data.withUnsafeBytes({(bytePointer: UnsafePointer<UInt8>) -> Int32 in
        bytePointer.advanced(by: start).withMemoryRebound(to: Int32.self, capacity: 4) { pointer in
          return pointer.pointee
        }
      })
      return Int32(littleEndian: intBits)
    }
    

    您可以对其进行修改、添加泛型等以适应其他原始类型(并根据数据字节的字节序改变它)。

    【讨论】:

    • 这确实是让我对 Swift 有点厌烦的地方。对于技术上极其简单的事情来说,这是极其复杂的事情。我想知道编译器是否优化了所有的 bs
    【解决方案4】:

    您可以扩展 Data 类型,创建一个泛型方法,获取字节并将其转换或根据需要显式设置结果类型:

    extension Data {
        func object<T>(at index: Index = 0) -> T {
            subdata(in: index..<self.index(index, offsetBy: MemoryLayout<T>.size))
            .withUnsafeBytes { $0.load(as: T.self) }
        }
    }
    

    extension Numeric {
        var data: Data {
            var source = self
            return Data(bytes: &source, count: MemoryLayout<Self>.size)
        }
    }
    

    let data = Data([0xFF, 0x1F])   // 2 bytes
    
    let uint16: UInt16 = data.object()      // 8191  littleEndian
    let number1 = uint16.littleEndian       // 8191
    let number2 = uint16.bigEndian          // 65311
    
    let int16 = data.object() as Int16      // 8191   littleEndian
    let number3 = int16.littleEndian        // 8191
    let number4 = int16.bigEndian           // -225
    
    print(number1) // 8191
    print(number2) // 65311
    print(number3) // 8191
    print(number4) // -225
    

    使用Int进行测试

    let random = Int.random(in: 1...100)    // 15 UInt32
    let data = random.data                  // 8 bytes  [15, 0, 0, 0, 0, 0, 0, 0]
    

    使用UInt32进行测试

    let random = UInt32.random(in: 1...100)  // 90 UInt32
    let data = random.data                   // 4 bytes  [90, 0, 0, 0]
    

    使用 Double 进行测试

    let random = Double.random(in: 0...1)  // 0.2463145485351322 Double
    let data = random.data                 // 8 bytes  [12, 99, 62, 49, 60, 135, 207, 63]
    

    如果要提取子数据:

    let data = Data([0xFF, 0x1F, 0x1F, 0xFF])      // 4 bytes
    
    let uint16: UInt16 = data.object(at: 2)        //  65311 littleEndian
    let number1 = uint16.littleEndian              // 65311
    let number2 = uint16.bigEndian                 // 8191
    
    let int16: Int16 = data.object(at: 2)          // -225   littleEndian
    let number3 = int16.littleEndian               // -225
    let number4 = int16.bigEndian                  // 8191
    
    number1 // 65311
    number2 // 8191
    number3 // -225
    number4 // 8191
    

    【讨论】:

      【解决方案5】:

      如果您经常这样做,您可能会发现此方法很有用:

      func readInteger<T : IntegerType>(data : NSData, start : Int) -> T {
          var d : T = 0
          data.getBytes(&d, range: NSRange(location: start, length: sizeof(T)))
          return d
      }
      

      该函数将数据中的起始位置作为参数以读取数字类型,并返回从您分配给它的任何内容推断出的 a 类型的值。

      例如:

      let i : UInt32 = readInteger(data, 10);
      

      从数据中的位置 10 读取一个 4 字节整数。

      如果将UInt32 更改为UInt16,它将读取两个字节。

      【讨论】:

      • 请详细说明如何使用它以及要传递的参数是什么...谢谢。
      【解决方案6】:

      数据要interger 感谢@rghome

      // MARK: - Extensions Data
      
      extension Data {
      
          /// To interger Data by range
          ///
          /// - Parameters:
          ///   - data:       Data
          ///   - startRange: StartRange
          ///   - endRange:   EndRange
          /// - Returns:      Integer Typed
          func toInterger<T : Integer>(withData data: NSData, withStartRange startRange: Int, withSizeRange endRange: Int) -> T {
              var d : T = 0
              (self as NSData).getBytes(&d, range: NSRange(location: startRange, length: endRange))
              return d
          }
      }
      
      101010
      let value:Int = Data().toInterger(withStartRange: 0, withEndRange: 2)
      get 10
      get 2 Int
      

      【讨论】:

        【解决方案7】:

        我对 swift 3.1 扩展的贡献:

        extension NSData{
            var int :  Int{
                var out: Int = 0
                self.getBytes(&out, length: MemoryLayout<Int>.size)
                return out
            }
        }
        

        只需调用 .int 即可获得您的价值,就像这样:

        let value = 50
        let data = NSData(bytes: &value, length: 4)
        print(data.int)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-01-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-24
          相关资源
          最近更新 更多