读取 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
}
我需要从 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
}