在Swift中,如何将现有的二进制文件读入数组?

时间:2023-01-23 17:01:21

As part of my projects, I have a binary data file consisting of a large series of 32 bit integers that one of my classes reads in on initialization. In my C++ library, I read it in with the following initializer:

作为我的项目的一部分,我有一个二进制数据文件,由一系列32位整数组成,我的一个类在初始化时读入。在我的C ++库中,我使用以下初始化程序读取它:

Evaluator::Evaluator() {
    m_HandNumbers.resize(32487834);
    ifstream inputReader;

    inputReader.open("/path/to/file/7CHands.dat", ios::binary);

    int inputValue;
    for (int x = 0; x < 32487834; ++x) {
        inputReader.read((char *) &inputValue, sizeof (inputValue));
        m_HandNumbers[x] = inputValue;
    }
    inputReader.close();
};

and in porting to Swift, I decided to read the entire file into one buffer (it's only about 130 MB) and then copy the bytes out of the buffer.

在移植到Swift时,我决定将整个文件读入一个缓冲区(它只有大约130 MB),然后将这些字节复制出缓冲区。

So, I've done the following:

所以,我做了以下事情:

public init() {
    var inputStream = NSInputStream(fileAtPath: "/path/to/file/7CHands.dat")!
    var inputBuffer = [UInt8](count: 32478734 * 4, repeatedValue: 0)
    inputStream.open()
    inputStream.read(&inputBuffer, maxLength: inputBuffer.count)
    inputStream.close()
}

and it works fine in that when I debug it, I can see inputBuffer contains the same array of bytes that my hex editor says it should. Now, I'd like to get that data out of there effectively. I know it's stored in.. whatever format you call it where the least significant bytes are first (i.e. the number 0x00011D4A is represented as '4A1D 0100' in the file). I'm tempted to just iterate through it manually and calculate the byte values by hand, but I'm wondering if there's a quick way I can pass an array of [Int32] and have it read those bytes in. I tried using NSData, such as with:

它运行正常,当我调试它时,我可以看到inputBuffer包含我的十六进制编辑器应该说的相同的字节数组。现在,我想有效地从那里获取数据。我知道它存储在......你称之为最低有效字节为止的任何格式(即数字0x00011D4A在文件中表示为'4A1D 0100')。我很想手动迭代它并手动计算字节值,但我想知道是否有一种快速方法可以传递[Int32]数组并让它读取这些字节。我尝试使用NSData,例如:

    let data = NSData(bytes: handNumbers, length: handNumbers.count * sizeof(Int32))
    data.getBytes(&inputBuffer, length: inputBuffer.count)

but that didn't seem to load the values (all the values were still zero). Can anyone please help me convert this byte array into some Int32 values? Better yet would be to convert them to Int (i.e. 64 bit integer) just to keep my variable sizes the same across the project.

但似乎没有加载值(所有值仍为零)。任何人都可以帮我把这个字节数组转换成一些Int32值吗?更好的是将它们转换为Int(即64位整数),以保持我的变量大小在整个项目中保持一致。

2 个解决方案

#1


3  

Not sure about your endian-ness, but I use the following function. The difference from your code is using NSRanges of the actual required type, rather than lengths of bytes. This routine reads one value at a time (it's for ESRI files whose contents vary field by field), but should be easily adaptable.

不确定你的endian-ness,但我使用以下函数。与代码的不同之处在于使用实际所需类型的NSRanges,而不是字节长度。该例程一次读取一个值(对于ESRI文件,其内容逐字段变化),但应该很容易适应。

func getBigIntFromData(data: NSData, offset: Int) -> Int {
    var rng = NSRange(location: offset, length: 4)
    var i = [UInt32](count: 1, repeatedValue:0)

    data.getBytes(&i, range: rng)
    return Int(i[0].bigEndian)// return Int(i[0]) for littleEndian
}

#2


2  

Grimxn provided the backbone of the solution to my problem, which showed me how to read sections of the buffer into an array; he then showed me a way to read the entire buffer in all at once. Rather than convert all of the items of the array needlessly to Int, I simply read the array into the buffer as UInt32 and did the casting to Int in the function that accesses that array.

Grimxn为我的问题提供了解决方案的主干,它向我展示了如何将缓冲区的各个部分读入数组;然后他告诉我一种方法可以同时读取整个缓冲区。我不是将数组的所有项目不必要地转换为Int,而是简单地将数组作为UInt32读入缓冲区,并在访问该数组的函数中对Int进行转换。

For now, since I don't have my utility class defined yet, I integrated Grimxn's code directly into my initializer. The class initializer now looks like this:

目前,由于我还没有定义我的实用程序类,我将Grimxn的代码直接集成到我的初始化程序中。类初始值设定项现在看起来像这样:

public class Evaluator {
    let HandNumberArraySize = 32487834

    var handNumbers: [Int32]

    public init() {
        let data = NSData(contentsOfFile: "/path/to/file/7CHands.dat")!
        var dataRange = NSRange(location: 0, length: HandNumberArraySize * 4)
        handNumbers = [Int32](count: HandNumberArraySize, repeatedValue: 0)
        data.getBytes(&handNumbers, range: dataRange)

        println("Evaluator loaded successfully")
    }

...

}

... and the function that references them is now:

...以及引用它们的函数现在是:

public func cardVectorToHandNumber(#cards: [Int], numberToUse: Int) -> Int {
    var output: Int

    output = Int(handNumbers[53 + cards[0] + 1])

    for i in 1 ..< numberToUse {
        output = Int(handNumbers[output + cards[i] + 1])
    }

    return Int(handNumbers[output])
}

Thanks to Grimxn and thanks once again to * for helping me in a very real way!

感谢Grimxn,再次感谢*以非常真实的方式帮助我!

#1


3  

Not sure about your endian-ness, but I use the following function. The difference from your code is using NSRanges of the actual required type, rather than lengths of bytes. This routine reads one value at a time (it's for ESRI files whose contents vary field by field), but should be easily adaptable.

不确定你的endian-ness,但我使用以下函数。与代码的不同之处在于使用实际所需类型的NSRanges,而不是字节长度。该例程一次读取一个值(对于ESRI文件,其内容逐字段变化),但应该很容易适应。

func getBigIntFromData(data: NSData, offset: Int) -> Int {
    var rng = NSRange(location: offset, length: 4)
    var i = [UInt32](count: 1, repeatedValue:0)

    data.getBytes(&i, range: rng)
    return Int(i[0].bigEndian)// return Int(i[0]) for littleEndian
}

#2


2  

Grimxn provided the backbone of the solution to my problem, which showed me how to read sections of the buffer into an array; he then showed me a way to read the entire buffer in all at once. Rather than convert all of the items of the array needlessly to Int, I simply read the array into the buffer as UInt32 and did the casting to Int in the function that accesses that array.

Grimxn为我的问题提供了解决方案的主干,它向我展示了如何将缓冲区的各个部分读入数组;然后他告诉我一种方法可以同时读取整个缓冲区。我不是将数组的所有项目不必要地转换为Int,而是简单地将数组作为UInt32读入缓冲区,并在访问该数组的函数中对Int进行转换。

For now, since I don't have my utility class defined yet, I integrated Grimxn's code directly into my initializer. The class initializer now looks like this:

目前,由于我还没有定义我的实用程序类,我将Grimxn的代码直接集成到我的初始化程序中。类初始值设定项现在看起来像这样:

public class Evaluator {
    let HandNumberArraySize = 32487834

    var handNumbers: [Int32]

    public init() {
        let data = NSData(contentsOfFile: "/path/to/file/7CHands.dat")!
        var dataRange = NSRange(location: 0, length: HandNumberArraySize * 4)
        handNumbers = [Int32](count: HandNumberArraySize, repeatedValue: 0)
        data.getBytes(&handNumbers, range: dataRange)

        println("Evaluator loaded successfully")
    }

...

}

... and the function that references them is now:

...以及引用它们的函数现在是:

public func cardVectorToHandNumber(#cards: [Int], numberToUse: Int) -> Int {
    var output: Int

    output = Int(handNumbers[53 + cards[0] + 1])

    for i in 1 ..< numberToUse {
        output = Int(handNumbers[output + cards[i] + 1])
    }

    return Int(handNumbers[output])
}

Thanks to Grimxn and thanks once again to * for helping me in a very real way!

感谢Grimxn,再次感谢*以非常真实的方式帮助我!