2014-10-07 12 views
7

w Objective-C kod wyglądał podobało i działał bez zarzutu,Konwersja NSData do Integer w Swift

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

Jak można to zrobić w Swift?

Odpowiedz

14

Jak to:

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 
+0

Absolutnie doskonały! Dziękuję Ci! – dhint4

+1

lub 'sizeofValue (losowe)' – newacct

+0

W Swift3: sizeof (NSInteger) staje się MemoryLayout . Rozmiar i println zostaje wydrukowany. – parleer

5

Można znaleźć to metoda przydatna, gdy robisz to dużo:

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 
} 

Funkcja przyjmuje jako parametr pozycji startowej w danych przeczytać typ numeryczny i zwraca wartość typu wywnioskowanego z tego, do czego je przypisujesz.

Na przykład

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

odczytuje liczbę całkowitą 4 bajtów na pozycji 10 w danych.

Po zmianie UInt32 na UInt16 będzie czytać dwa bajty.

+0

proszę, opracuj trochę więcej o tym, jak z niego korzystać i jakie parametry przejść ... dziękuję. –

6

Dla Swift 3, można to zrobić (little endian, ale podobna do 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) 
} 

Można modyfikować tego dodać rodzajowych, itd., Aby dopasować inne typy pierwotne (i zmieniać go w zależności od endianność bajtów danych).

+0

To jest naprawdę coś, co irytuje mnie trochę o Swift. To niezwykle skomplikowane rzeczy dla czegoś, co jest technicznie niezwykle proste. Zastanawiam się, czy kompilator optymalizuje wszystkie bs z dala –

0

Dane interger dzięki @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

dzięki @rghome :) – YannickSteph

0

Mój wkład z szybkiej 3.1 Rozszerzenie:

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

Wystarczy zadzwonić .int aby uzyskać wartość, podobnie jak to:

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

In Swift 4:

Jest kilka rzeczy do rozważenia przy wyodrębnianiu wartości całkowitych ze strumienia Data. Signedness i Endianess. Więc wymyśliłem funkcję w rozszerzeniu do Data, która podaje Signedness z typu liczby całkowitej, którą chcesz wyodrębnić i przekazuje Endianess i Index jako parametry. Typy liczb całkowitych, które można wyodrębnić, są zgodne z protokołem FixedWidthInteger.

przypomnienia: Funkcja ta sprawdza, czy zakres Index znajduje się wewnątrz granic bufora Data więc może ulec awarii w zależności od rozmiaru, typu wyodrębnione w stosunku do końca bufora.

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

przykład:

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) 

Wyniki:

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

Obserwuj wywołania funkcji, aby sprawdzić, jaki typ wywodu został pobrany. Oczywiście Endianess nie ma sensu dla Int8 lub UInt8, ale funkcja działa zgodnie z oczekiwaniami.

W razie potrzeby wartości można później przenieść na numer Int.

+0

Witam - Czy masz znacznie prostsze rozwiązanie do konwersji danych <02> na UInt8 2 lub Int 2? – richc

+1

@richc: Najprostszym sposobem uzyskania 'UInt8' jest użycie indeksu dolnego, jeśli dokładnie znasz indeks żądanego bajtu:' data [indeks] ' –