读取 Swift 中的二进制 (.hgt) 文件(将代码从 c++ 迁移到 swift)

Read binary (.hgt) file in Swift (migrate code from c++ to swift)

我需要从 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);
}

#define SIZE 1201

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

let size = 1201

下一个:

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

这将打开一个文件进行读取。我们能做到这一点。在 'defer' 块中关闭文件,这样无论如何,文件都会在我们完成后关闭。

// 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])
    }
}

认为应该这样做。

我最近在解决同样的问题,但发现 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([=10=]) }
    }

    return matrix
}