【问题标题】:How to read a block of data from file (instead of a whole file) in Swift如何在 Swift 中从文件(而不是整个文件)中读取数据块
【发布时间】:2019-09-16 22:59:11
【问题描述】:

假设我有一个仅包含 ASCII 字符的 8 字节长的文件:brownfox。

我不想加载整个文件并处理 if,我不想加载 2 字节的块 [UInt8] 并对 2 字节大小的块进行操作,因此操作如下:

  1. 从文件加载br(不是整个文件)
  2. 对数据执行任何操作,例如反向到rb
  3. 将输出保存到另一个文件
  4. 重复:ownfox

这背后的原因: 这样,如果我处理 1GB 的文本文件,我实际上不必有 1GB 的可用 RAM(或 2GB 用于输入和输出文件)。

这种文件处理方法对于加密和发送到云解决方案对我来说很重要。

我正在使用这个扩展:

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) 总是从第一个字节开始。例如,如何从第 16 个字节读取到第 20 个字节?

InputStream.read(_:maxLength:) 上的文档

从当前读取索引,最多占用指定的字节数 在流的第二个参数中并将它们放在 客户端提供的缓冲区(第一个参数)。缓冲区必须是 大小由第二个参数指定。返回实际数量 放置在缓冲区中的字节;如果流中没有任何东西, 返回 0。将索引重置到流中以进行下一次读取操作。

我该怎么做才能不重置索引,并从上一个结束的地方获取下一个操作?

【问题讨论】:

    标签: swift


    【解决方案1】:

    使用FileHandle。您可以打开文件句柄进行读取。然后使用seek(toFileOffset:) 设置您希望读取的位置。然后使用readData(ofLength:) 得到一些Data。完成后请务必关闭文件句柄。

    【讨论】:

      【解决方案2】:

      rmaddy 的解决方案奏效了!

      对于任何会遇到同样问题的人来说,这是一个非常粗略的 sn-p。这不是一个确切的答案,但它显示了需要完成的所有事情:)

          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
          }
      

      【讨论】:

      • 我很确定您不需要调用seek,因为readData 移动了文件指针,而您的下一次读取就在它停止的地方。
      • 您还应该重新审视您对nil 的处理。在fileHandle 上同时使用! 和?。在这种情况下,两者都不是理想的,但至少是一致的。
      • 上面的代码只是一个概念证明。实际代码解决了这个问题:) 我做了一个编辑来展示它的样子。而且,是的,我知道,我可以做得比抛出一个通用的 NSError 更好:D
      猜你喜欢
      • 2011-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多