Reputation: 1914
Let's say I've got a file that is 8 bytes long containing only ASCII characters: brownfox
.
Instead of loading the whole file and working on if, I wan't to load block of 2 bytes [UInt8]
and do operation on 2-bytes sized blocks, so the operations goes as follow:
br
from file (and not the whole file)rb
ow
nf
ox
Reasoning behind this: This way if I handle a file that is 1GB of text I don't have to actualy have 1GB of RAM free (or 2GB for input and output file).
This method for files handling is important to me for encryption and sending to cloud solutions.
I'm using this extension:
extension Data {
/**
Consumes the specified input stream, creating a new Data object
with its content.
- Parameter reading: The input stream to read data from.
- Note: Closes the specified stream.
*/
init(reading input: InputStream) {
self.init()
input.open()
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
while input.hasBytesAvailable {
let read = input.read(buffer, maxLength: bufferSize)
self.append(buffer, count: read)
}
buffer.deallocate()
input.close()
}
/**
Consumes the specified input stream for up to `byteCount` bytes,
creating a new Data object with its content.
- Parameter reading: The input stream to read data from.
- Parameter byteCount: The maximum number of bytes to read from `reading`.
- Note: Does _not_ close the specified stream.
*/
init(reading input: InputStream, for byteCount: Int) {
self.init()
input.open()
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: byteCount)
let read = input.read(buffer, maxLength: byteCount)
self.append(buffer, count: read)
buffer.deallocate()
}
}
But init(reading input: InputStream, for byteCount: Int)
always goes from 1st byte. How can I read for example from 16th to 20th byte?
Documentation on InputStream.read(_:maxLength:)
From the current read index, take up to the number of bytes specified in the second parameter from the stream and place them in the client-supplied buffer (first parameter). The buffer must be of the size specified by the second parameter. Return the actual number of bytes placed in the buffer; if there is nothing left in the stream, return 0. Reset the index into the stream for the next read operation.
What can I do to not reset the index, and get the next operation from the place where the previous ended?
Upvotes: 7
Views: 5259
Reputation: 1914
rmaddy's solution worked!
Here's a very rough snippet for anybody that will came here with the same problem. It's not an exact answer, but it shows everything that needs to be done :)
func loadInBlocks(path: String) -> [Data] {
var blocks = [Data]()
let correctPath = //path to file
let fileHandle = FileHandle(forReadingAtPath: correctPath)
let dataFromFirstByteTo4thByte = fileHandle!.readData(ofLength: 4)
blocks.append(dataFromFirstByteTo4thByte)
fileHandle?.seek(toFileOffset: 4)
let dataFrom5thByteTo8thByte = fileHandle!.readData(ofLength: 4)
blocks.append(dataFrom5thByteTo8thByte)
fileHandle?.closeFile()
return blocks
}
and the actual usage:
func loadBlock(number: Int, withBlockSize size: Int, path: String) throws -> Data {
let correctPath = path.replacingOccurrences(of: "file://", with: "").replacingOccurrences(of: "%20", with: " ")
guard let fileHandle = FileHandle(forReadingAtPath: correctPath) else { throw NSError() }
let bytesOffset = UInt64((number-1) * size)
fileHandle.seek(toFileOffset: bytesOffset)
let data = fileHandle.readData(ofLength: size)
fileHandle.closeFile()
return data
}
Upvotes: 4
Reputation: 318954
Use FileHandle
. You can open the file handle for reading. Then use seek(toFileOffset:)
to set where you wish to read from. Then use readData(ofLength:)
to get some Data
. Be sure to close the file handle when done.
Upvotes: 8