Converting NSData to Integer in Swift

后端 未结 7 2082
予麋鹿
予麋鹿 2021-01-05 02:29

In Objective-C the code looked liked this and worked flawlessly,

NSInteger random = arc4random_uniform(99) + 1 
NSData *data = [NSData dataWithBytes:& ra         


        
相关标签:
7条回答
  • 2021-01-05 03:05

    For Swift 3, you can do this (little endian, but similar for big):

    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)
    }
    

    You can modify this, add generics, etc. to fit other primitive types (and vary it depending on the endianness of the data bytes).

    0 讨论(0)
  • 2021-01-05 03:08

    You might find this method useful if you are doing it a lot:

    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
    }
    

    The function takes as a parameter the start position in the data to read the numeric type and it returns a value of the a type inferred from whatever you are assigning it to.

    For example:

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

    reads an 4 byte integer from position 10 in the data.

    If you change UInt32 to UInt16 it will read two bytes.

    0 讨论(0)
  • 2021-01-05 03:14

    My contribution with a swift 3.1 extension :

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

    Just call .int to get your value, just like this :

    let value = 50
    let data = NSData(bytes: &value, length: 4)
    print(data.int)
    
    0 讨论(0)
  • 2021-01-05 03:19

    You can extend Data type, create a generic method, get the bytes and cast it or set the resulting type explicitly as needed:

    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
    

    Testing with Int

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

    Testing with UInt32

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

    Testing with 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]
    

    If you want to extract a subdata:

    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
    
    0 讨论(0)
  • 2021-01-05 03:20

    Data to interger thanks @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
    
    0 讨论(0)
  • 2021-01-05 03:23

    In Swift 4:

    There are a couple of things to consider when extracting an integer value from a Data stream. Signedness and Endianess. So I came up with a function in an extension to Data that infers Signedness from the type of integer you want to extract and passes Endianess and Index as parameters. The types of integers that can be extracted are all that conform to FixedWidthInteger protocol.

    Reminder: This function will not check if the Index range is inside the bounds of the Data buffer so it may crash depending on the size of the type being extracted in relation to the end of the buffer.

    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
            }
        }
    }
    

    Example:

    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)
    

    Results:

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

    Observe the function calls to see how the type to be extracted is inferred. Of course Endianess doesn't make sense for Int8 or UInt8, but the function works as expected.

    Values can later be cast to Int if needed.

    0 讨论(0)
提交回复
热议问题