3

In my app documents folder I have a file which I am trying to read byte-per-byte into an array of UInt8 where each element represents a byte. How would I go about doing this? The file happens to be called Q1.dat.

Here's my unsuccessful attempt:

func readArray() -> [Int]? {
    if let arrayPath: String = createArrayPath() {
        if let arrayFromFile: [Int] = NSArray(contentsOfFile: arrayPath) as? [Int] {
            return arrayFromFile
        }
    }
    return nil
}

func createArrayPath () -> String? {
    if let docsPath: String = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last {
        return ((docsPath as NSString).stringByAppendingPathComponent("Q1") as NSString).stringByAppendingPathExtension("dat")
    }
    return nil
}
Lahav
  • 781
  • 10
  • 22

1 Answers1

7

Try something like this:

let filePath = "/Path/To/Directory/Q1.dat"

var bytes = [UInt8]()

if let data = NSData(contentsOfFile: filePath) {

    var buffer = [UInt8](count: data.length, repeatedValue: 0)
    data.getBytes(&buffer, length: data.length)
    bytes = buffer
}

I'm sorry, I'm not in a place where I can test it, but with a little tinkering it should give you an array of UInt8s, each of which represents a byte from the file.

EDIT: And if you really want this to be an array of Ints instead of UInt8s, just map bytes to an array of Ints:

let intBytes = bytes.map { Int($0) }

...although generally speaking it is not clear why you want to work with anything other than a collection of bytes, which are most appropriately represented as a series of UInt8s.

And if you really, really, want the ones and zeros, then tacking this on to the end should do it:

var onesAndZeros = [UInt8]()

bytes.forEach { byte in

    var array = [UInt8](count: 8, repeatedValue: 0)

    let bits = String(byte, radix: 2).characters.flatMap { UInt8(String($0)) }

    array.replaceRange((8-bits.count)..<8, with: bits)
    onesAndZeros.appendContentsOf(array)
}

Again, I went with UInt8 instead of Int, just because it makes more sense, but change it up as you need.

Just to neaten things up, you can wrap the two operations in a couple of functions like this:

func bytesFromFile(filePath: String) -> [UInt8]? {

    guard let data = NSData(contentsOfFile: filePath) else { return nil }

    var buffer = [UInt8](count: data.length, repeatedValue: 0)
    data.getBytes(&buffer, length: data.length)

    return buffer
}

func bitsFromBytes(bytes: [UInt8]) -> [UInt8] {

    var onesAndZeros = [UInt8]()

    bytes.forEach { byte in

        var array = [UInt8](count: 8, repeatedValue: 0)

        let bits = String(byte, radix: 2).characters.flatMap { UInt8(String($0)) }

        array.replaceRange((8-bits.count)..<8, with: bits)
        onesAndZeros.appendContentsOf(array)
    }

    return onesAndZeros
}
Aaron Rasmussen
  • 13,082
  • 3
  • 42
  • 43
  • `let intBytes = bytes.map { Int($0) }` converts each single byte to an integer and does not take 4 bytes to form a single UInt32. – qwerty_so Nov 12 '16 at 21:32