如何在 Swift 中将数据转换为 Doubles、Ints 和 Strings 等类型?

Posted

技术标签:

【中文标题】如何在 Swift 中将数据转换为 Doubles、Ints 和 Strings 等类型?【英文标题】:How can I convert data into types like Doubles, Ints and Strings in Swift? 【发布时间】:2017-04-05 21:21:24 【问题描述】:

我正在 ios Swift 中为 shapefile(一种 GIS 格式,与这个问题并不特别相关)构建一个自定义文件打开器。这些文件有一个 100 字节长的标头。我能够将其读入 4 字节数组,其中存储我想要的信息。我可以将这些数组转换为 Swift 类型 DataNSData,并有一些其他选项可以转换它们(例如 Base64EncodedString)。但我无法将这些原始数组或数据或任何格式转换为有用的属性,如DoubleIntString

import Foundation
    struct ShapeReader 
        var shapeFile = FileHandle(forReadingAtPath: "/Users/christopherjlowrie/Documents/Shapes/SF_Neighborhoods/Planning_Zones.shp")
        var fileHeader: String
            let header = shapeFile?.readData(ofLength: 100)
            let headerStream = InputStream(data: header!)
            headerStream.open()
            var buffer = [UInt8](repeating: 0, count: 4)
            while (headerStream.hasBytesAvailable)
                headerStream.read(&buffer, maxLength: buffer.count)
                print(buffer)
                let x = Data(buffer)
                print(x)
        
        return "A"
    

这目前只返回 A,因为出于测试原因,我让它返回一个字符串

如何在 Swift 中打开文件并将其原始字节读入类型(DoublesIntsStrings)?

【问题讨论】:

【参考方案1】:

Xcode 11 • Swift 5.1 或更高版本

要从String 或任何Numeric 类型转换为Data

extension StringProtocol 
    var data: Data  .init(utf8) 


extension Numeric 
    var data: Data 
        var source = self
        // This will return 1 byte for 8-bit, 2 bytes for 16-bit, 4 bytes for 32-bit and 8 bytes for 64-bit binary integers. For floating point types it will return 4 bytes for single-precision, 8 bytes for double-precision and 16 bytes for extended precision.
        return .init(bytes: &source, count: MemoryLayout<Self>.size)
    


Data(字节)转换回String

extension DataProtocol 
    var string: String?  String(bytes: self, encoding: .utf8) 


Data 转换回通用Numeric

extension Numeric 
    init<D: DataProtocol>(_ data: D) 
        var value: Self = .zero
        let size = withUnsafeMutableBytes(of: &value,  data.copyBytes(to: $0) )
        assert(size == MemoryLayout.size(ofValue: value))
        self = value
    


extension DataProtocol 
    func value<N: Numeric>() -> N  .init(self) 


let value = 12.34                      // implicit Double 12.34
let data = value.data                  // double data - 8 bytes
let double = Double(data)              // implicit Double 12.34
let double1: Double = .init(data)      // explicit Double 12.34
let double2: Double = data.value()     // explicit Double 12.34
let double3 = data.value() as Double   // casting to Double 12.34

现在我们可以轻松地为每个Numeric 类型添加一个属性:

extension DataProtocol 
    var integer: Int  value() 
    var int32: Int32  value() 
    var float: Float  value() 
    var cgFloat: CGFloat  value() 
    var float80: Float80  value() 
    var double: Double  value() 
    var decimal: Decimal  value() 


游乐场测试

let intData = 1_234_567_890_123_456_789.data    // 8 bytes (64 bit Integer)
let dataToInt: Int = intData.integer                 // 1234567890123456789

let intMinData = Int.min.data                   // 8 bytes (64 bit Integer)
let backToIntMin = intMinData.integer           // -9223372036854775808

let intMaxData = Int.max.data                   // 8 bytes (64 bit Integer)
let backToIntMax = intMaxData.integer           // 9223372036854775807

let myInt32Data = Int32(1_234_567_890).data     // 4 bytes (32 bit Integer)
let backToInt32 = myInt32Data.int32             // 1234567890

let int32MinData = Int32.min.data               // 4 bytes (32 bit Integer)
let backToInt32Min = int32MinData.int32         // -2147483648

let int32MaxData = Int32.max.data               // 4 bytes (32 bit Integer)
let backToInt32Max = int32MaxData.int32         // 2147483647

let myFloatData = Float.pi.data                 // 4 bytes (32 bit single=precison FloatingPoint)
let backToFloat = myFloatData.float             // 3.141593
backToFloat == .pi      // true

let myCGFloatData = CGFloat.pi.data                 // 4 bytes (32 bit single=precison FloatingPoint)
let backToCGFloat = myCGFloatData.cgFloat             // 3.141593
backToCGFloat == .pi      // true

let myDoubleData = Double.pi.data               // 8 bytes (64 bit double-precision FloatingPoint)
let backToDouble = myDoubleData.double          // 3.141592653589793
backToDouble == .pi     // true

let myFloat80Data = Float80.pi.data             // 16 bytes (128 bit extended-precision FloatingPoint)
let backToFloat80 = myFloat80Data.float80       // 3.141592653589793116
backToFloat80 == .pi    // true

let decimalData = Decimal.pi.data             // 20 bytes Decimal type
let backToDecimal = decimalData.decimal       // 3.14159265358979323846264338327950288419
backToDecimal == .pi    // true

let stringBytes = "Hello World !!!".data.prefix(4)  // 4 bytes
let backToString = stringBytes.string               //  "Hell"

【讨论】:

后续问题: 转换为 double 无法按预期工作。返回值不是在 100 左右翻倍,而是很小(在 e-325 的范围内)。这可能与 Big Endian/Little Endian 有关。你有什么解决方案或我可以开始寻找的地方吗? @ChrisLowrie 你能发布数据输入、类型和预期输出吗?你确定你的输入是双 64 位(8 位)并且输出也是双倍的吗?在您只有 4 个字节时,只需创建一个浮点数并从中初始化一个双精度数。 Double(float) 我已经测试了双重扩展,它确实按预期工作。你需要显示你的实际代码,否则我只是猜测 @ChrisLowrie 是的,字节序是您麻烦的根源,如果您无权访问 ESRI 文档,请至少检查一下en.wikipedia.org/wiki/Shapefile @Klaas 这取决于您访问数据字节的方式。您可能正在通过下标访问字节。您可以使用 Data 的 subdata 方法来避免此问题。 ***.com/a/47530223/2303865

以上是关于如何在 Swift 中将数据转换为 Doubles、Ints 和 Strings 等类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中将 JSON 转换为数据类型?

如何在 Swift 中将数值数据数组转换为 RAW 图像数据?

如何在 Swift 5 中将“日期”转换为“数据”,反之亦然? [复制]

如何在 Swift 3 中将 NSData 转换为数据?

在 Swift 中将 JSON 数据转换为 Double

如何在 Swift 中将可转换属性保存到 Core Data