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

时间:2017-08-13 20:21:54

标签: c swift migrate

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

2 个答案:

答案 0 :(得分:3)

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

认为应该这样做。

答案 1 :(得分:0)

我最近正在实施相同的问题,但是发现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
}