Как прочитать блок данных из файла (вместо целого файла) в Swift - PullRequest
2 голосов
/ 27 апреля 2019

Допустим, у меня есть файл длиной 8 байтов, содержащий только символы ASCII: brownfox.

Вместо загрузки всего файла и работы с if, я не хочу загружать блок из 2 байтов [UInt8] и выполнять операции с блоками размером 2 байта, поэтому операции выполняются следующим образом:

  1. загрузить br из файла (а не весь файл)
  2. выполнить любые действия с данными, например, вернуться к rb
  3. сохранить вывод в другой файл
  4. повторить для: ow nf ox

Причина этого: Таким образом, если я обрабатываю файл объемом 1 ГБ, мне фактически не нужно иметь 1 ГБ свободной памяти (или 2 ГБ для файла ввода и вывода).

Этот метод для обработки файлов важен для меня для шифрования и отправки в облачные решения.

Я использую это расширение:

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

Но init(reading input: InputStream, for byteCount: Int) всегда идет от 1-го байта. Как я могу прочитать, например, с 16-го по 20-й байт?

Документация по InputStream.read(_:maxLength:)

Из текущего индекса чтения возьмите до указанного количества байтов во втором параметре из потока и поместите их в предоставленный клиентом буфер (первый параметр). Буфер должен быть размер указан вторым параметром. Вернуть фактическое количество байты, помещенные в буфер; если в потоке ничего не осталось, return 0. Сбросить индекс в поток для следующей операции чтения.

Что я могу сделать, чтобы не сбросить индекс и получить следующую операцию из того места, где закончилась предыдущая?

Ответы [ 2 ]

1 голос
/ 27 апреля 2019

Используйте FileHandle. Вы можете открыть дескриптор файла для чтения. Затем используйте seek(toFileOffset:), чтобы установить, откуда вы хотите читать. Затем используйте readData(ofLength:), чтобы получить Data. После завершения обязательно закройте дескриптор файла.

0 голосов
/ 28 апреля 2019

Решение Рмадди сработало!

Вот очень грубый фрагмент для любого, кто придет сюда с той же проблемой. Это не точный ответ, но он показывает все, что нужно сделать:)

    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
    }

и фактическое использование:

    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
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...