将结构写入文件的最佳方法是什么?

问题描述:

我有这样两个结构:将结构写入文件的最佳方法是什么?

struct pcap_hdr_s { 
    UInt32 magic_number; 
    UInt16 version_major; 
    UInt16 version_minor; 
    int32_t thiszone; 
    UInt32 sigfigs; 
    UInt32 snaplen; 
    UInt32 network; 
}; 
//packet header 
struct pcaprec_hdr_s { 
    UInt32 ts_sec; 
    UInt32 ts_usec; 
    UInt32 incl_len; 
    UInt32 orig_len; 
}; 

被初始化(例如)如下:

let pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number: 0xa1b2c3d4, 
              version_major: 2, 
              version_minor: 4, 
              thiszone: 0, 
              sigfigs: 0, 
              snaplen: 
              pcap_record_size, 
              network: LINKTYPE_ETHERNET) 

    let pcapRecHeader : pcaprec_hdr_s = pcaprec_hdr_s(ts_sec: UInt32(ts.tv_sec), 
             ts_usec: UInt32(ts.tv_nsec), 
             incl_len: plen, 
             orig_len: length) 

我试图创建的结构是这样的数据/ NSData的对象:

  //write pcap header 
      let pcapHeaderData : NSData = NSData(bytes: pcapHeader, length: sizeofValue(pcapHeader)) 
      //write pcaprec header 
      let pcapRecHeaderData : NSData = NSData(bytes: pcapRecHeader, length: sizeofValue(pcapRecHeader)) 

但我总是得到这条错误的每一行:

"Connot convert value if type 'pcap_hdr_s' to expected arguemnt type 'UsafeRawPointer?'" 

我看了一下在Swift中的UnsafeRawPointers的文档,但我没有得到足够的,现在,从结构创建NSData对象。 我在正确的路上,还是有更好的完成我的打算?

如果此数据初始化会的工作,我的下一个步骤将是

  • 追加pcapRecHeaderData到pcapHeaderData
  • 写pcapHeaderData原子到文件/ URL与Data/NSData的的

所提供的功能编辑:

//packet ethernet header 
struct ethernet_hdr_s { 
    let dhost : [UInt8] 
    let shost : [UInt8] 
    let type : UInt16 
}; 

let src_mac : [UInt8] = [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB] 
let dest_mac : [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55] 

let ethernetHeader : ethernet_hdr_s = ethernet_hdr_s(dhost: dest_mac, shost: src_mac, type: 0x0800) 

编辑2:

let payloadSize = packet.payload.count 
      let plen = (payloadSize < Int(pcap_record_size) ? payloadSize : Int(pcap_record_size)); 

      bytesWritten = withUnsafePointer(to: &(packet.payload)) { 
       $0.withMemoryRebound(to: UInt8.self, capacity: Int(plen)) { 
        ostream.write($0, maxLength: Int(plen)) 
       } 
      } 
      if bytesWritten != (Int(plen)) { 
       // Could not write all bytes, report error ... 
       NSLog("error in Writting packet payload, not all Bytes written: bytesWritten: %d|plen: %d", bytesWritten, Int(plen)) 
      } 

你可以写任意数据到InputStream无需先创建一个 (NS)Data对象。“挑战”是如何将指针转换 的结构到UInt8指针由write方法预期:

let ostream = OutputStream(url: url, append: false)! // Add error checking here! 
ostream.open() 

var pcapHeader = pcap_hdr_s(...) 
let headerSize = MemoryLayout.size(ofValue: pcapHeader) 

let bytesWritten = withUnsafePointer(to: &pcapHeader) { 
     $0.withMemoryRebound(to: UInt8.self, capacity: headerSize) { 
     ostream.write($0, maxLength: headerSize) 
    } 
} 
if bytesWritten != headerSize { 
    // Could not write all bytes, report error ... 
} 

以同样的方式,你可以从InputStream读取数据:

let istream = InputStream(url: url)! // Add error checking here! 
istream.open() 

let bytesRead = withUnsafeMutablePointer(to: &pcapHeader) { 
    $0.withMemoryRebound(to: UInt8.self, capacity: headerSize) { 
     istream.read($0, maxLength: headerSize) 
    } 
} 
if bytesRead != headerSize { 
    // Could not read all bytes, report error ... 
} 

如果该文件可能在不同的平台上创建了一个带有 不同的字节顺序,那么你可以选中“魔术师”和交换字节 如果必要的话(如在https://wiki.wireshark.org/Development/LibpcapFileFormat描述):

switch pcapHeader.magic_number { 
case 0xa1b2c3d4: 
    break // Already in host byte order 
case 0xd4c3b2a1: 
    pcapHeader.version_major = pcapHeader.version_major.byteSwapped 
    pcapHeader.version_minor = pcapHeader.version_minor.byteSwapped 
    // ... 
default: 
    // Unknown magic, report error ... 
} 

为了简化写入和读出结构的任务的一个可限定 自定义扩展方法,例如

extension OutputStream { 

    enum ValueWriteError: Error { 
     case incompleteWrite 
     case unknownError 
    } 

    func write<T>(value: T) throws { 
     var value = value 
     let size = MemoryLayout.size(ofValue: value) 
     let bytesWritten = withUnsafePointer(to: &value) { 
      $0.withMemoryRebound(to: UInt8.self, capacity: size) { 
       write($0, maxLength: size) 
      } 
     } 
     if bytesWritten == -1 { 
      throw streamError ?? ValueWriteError.unknownError 
     } else if bytesWritten != size { 
      throw ValueWriteError.incompleteWrite 
     } 
    } 
} 

extension InputStream { 

    enum ValueReadError: Error { 
     case incompleteRead 
     case unknownError 
    } 

    func read<T>(value: inout T) throws { 
     let size = MemoryLayout.size(ofValue: value) 
     let bytesRead = withUnsafeMutablePointer(to: &value) { 
      $0.withMemoryRebound(to: UInt8.self, capacity: size) { 
       read($0, maxLength: size) 
      } 
     } 
     if bytesRead == -1 { 
      throw streamError ?? ValueReadError.unknownError 
     } else if bytesRead != size { 
      throw ValueReadError.incompleteRead 
     } 
    } 
} 

现在你可以读写简单地

try ostream.write(value: pcapHeader) 
try istream.read(value: &pcapHeader) 

当然,这仅适用于“自足”结构喜欢你 pcap_hdr_spcaprec_hdr_s

+0

感谢这个很好的答案,但我被卡在了传递OutputStream文件url的地步。在将URL传递给OutputStream之前是否创建了文件,还是由流本身创建了该文件? – student96

+0

@ student96:我认为现在已经澄清:) –

+0

是的,谢谢你的其他答案。我虽然它不是同一个问题,所以我创建了一个新的线程;) – student96

您可以转换pcap_hdr_sData,反之亦然斯威夫特卡伦特3

  • pcap_hdr_s - >Data

    var pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number ... 
    
    let data = withUnsafePointer(to: &pcapHeader) { 
        Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: pcapHeader)) 
    } 
    
  • Data - >pcap_hdr_s

    let header: pcap_hdr_s = data.withUnsafeBytes { $0.pointee } 
    

参考:round trip Swift number types to/from Data

+0

如果(当,当)ABI发生变化时,保存这样的数据将会中断 – Alexander

+0

@Alexander什么是ABI? – vadian

+0

ABI是“应用程序二进制接口”。 - @亚历山大:pcap_hdr_s结构(在https://wiki.wireshark.org/Development/LibpcapFileFormat记录)有一个“魔术”和版本号,它应该足以防止读取不兼容的数据。 –