【问题标题】:Read binary (.hgt) file in Swift (migrate code from c++ to swift)在 Swift 中读取二进制 (.hgt) 文件(将代码从 c++ 迁移到 swift)
【发布时间】:2018-01-21 16:57:07
【问题描述】:

我需要从 Swift 中的二进制 .hgt 文件中读取高程数据。我找到了this result for c,但我无法将它迁移到 Swift。

#include <stdio.h>

#define SIZE 1201
signed short int matrix[SIZE][SIZE] = {0};

int main(int argc, const char * argv[])
{
FILE *fp = fopen("N49E013.hgt", "rb");    

unsigned char buffer[2];
for (int i = 0; i < SIZE; ++i)
{
    for (int j = 0; j < SIZE; ++j) 
    {
        if (fread(buffer, sizeof(buffer), 1, fp) != 1)
        {
            printf("Error reading file!\n");
            system("PAUSE");
            return -1;
        }
        matrix[i][j] = (buffer[0] << 8) | buffer[1];       
    }
}

fclose(fp);
}

【问题讨论】:

    标签: c swift migrate


    【解决方案1】:

    #define SIZE 1201

    这定义了一个名为“SIZE”的常量,所以这样做:

    let size = 1201

    下一个:

    FILE *fp = fopen("N49E013.hgt", "rb");

    这会打开一个文件以供阅读。我们能做到这一点。在“延迟”块中关闭文件,这样无论如何,完成后文件都会关闭。

    // change the path below to the correct path
    let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: "/path/to/N49E013.hgt"))
    defer { handle.closeFile() }
    

    现在,构建矩阵。我们要创建size 数量的数组,每个数组都有size 元素,从文件中读取。原来使用了两个嵌套的 for 循环,但 Swift 支持函数式编程结构,我们可以用它来更优雅地做到这一点:

    let matrix = try (0..<size).map { _ in
        try (0..<size).map { _ -> Int in
            // Unfortunately, FileHandle doesn't have any decent error-reporting mechanism
            // other than Objective-C exceptions.
            // If you need to catch errors, you can use fread as in the original,
            // or use an Objective-C wrapper to catch the exceptions.
    
            let data = handle.readData(ofLength: 2)
    
            if data.count < 2 { throw CocoaError(.fileReadCorruptFile) }
    
            return (Int(data[0]) << 8) | Int(data[1])
        }
    }
    

    认为应该这样做。

    【讨论】:

      【解决方案2】:

      我最近正在实施同样的问题,但发现 Charles Srstka 提供的解决方案有点慢。在 2016 年末 15" MBP 上加载一个文件大约需要 10 秒。

      我对其进行了一些调整,使用直接访问内存并按行而不是 2 个字节读取它,使其速度提高了大约 50 倍。

      static let size = 1201
      
      static func read(from path: String) throws -> [[UInt16]] {
      
          let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
      
          defer { handle.closeFile() }
      
          // Calculate all the necessary values
          let unitSize = MemoryLayout<UInt16>.size
          let rowSize = size * unitSize
          let expectedFileSize = size * rowSize
      
          // Get fileSize
          let fileSize = handle.seekToEndOfFile()
      
          // Check file size
          guard fileSize == expectedFileSize else {
              throw CocoaError(.fileReadCorruptFile)
          }
      
          // Go back to the start
          handle.seek(toFileOffset: 0)
      
          // Iterate
          let matrix: [[UInt16]] = (0..<size).map { _ in
              // Read a row
              let data = handle.readData(ofLength: rowSize)
              // With bytes...
              let row: [UInt16] = data.withUnsafeBytes { (bytes: UnsafePointer<UInt16>) -> [UInt16] in
                  // Get the buffer. Count isn't using rowSize because it calculates number of bytes based on data type
                  let buffer = UnsafeBufferPointer<UInt16>(start: bytes, count: size)
                  // Create an array
                  return Array<UInt16>(buffer)
              }
              // Return row, swapping from Little to Big endian
              return row.map { CFSwapInt16HostToBig($0) }
          }
      
          return matrix
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-03
        相关资源
        最近更新 更多