首页 > 解决方案 > 如何使用文件描述符快速转移写入文件?

问题描述

我想使用一些使用文件描述符的 C 代码。背景是我想从 cgraph 库中读取一些数据。

public extension UnsafeMutablePointer where Pointee == Agraph_t {
   func saveTo(fileName: String)  {
      let f = fopen(cString(fileName), cString("w"))
      agwrite(self,f)
      fsync(fileno(f))
      fclose(f)
   }
}

我想要文件输出,但不写入临时文件。因此,我想做这样的事情:

public extension UnsafeMutablePointer where Pointee == Agraph_t {
    var asString: String  {
        let pipe = Pipe()
        let fileDescriptor = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
        fileDescriptor.pointee = pipe.fileHandleForWriting.fileDescriptor
        agwrite(self, fileDescriptor)
        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        if let output = String(data: data, encoding: .utf8) {
            return output  
        }  
        return ""
    }
}

但它不起作用,导致 agwrite(,) 中出现 EXC_BAD_ACCESS。我需要做什么?提前谢谢了!

标签: swiftmacoslanguage-interoperability

解决方案


文件描述符和文件指针不是一回事FILE *这很令人困惑,并且因为这个符号而对谷歌来说真的很难的事实更加令人沮丧。

您需要fdopen文件描述符 ( pipe.fileHandleForWriting.fileDescriptor) 来接收FILE *(UnsafeMutablePointer<FILE>在 Swift 中)。这就是你然后传递给agwrite.

fclose当您完成写入文件指针时,它对文件指针很重要,否则.readDataToEndOfFile()将永远不会终止。我做了一个辅助函数来确保fclose不会被遗忘。有可能agwrite在内部关闭文件指针本身。如果是这种情况,您应该删除此代码并只给它一个fdopen简单明了的结果。

import Foundation

public typealias Agraph_t = Int // Dummy value

public struct AGWriteWrongEncoding: Error { }

func agwrite(_: UnsafeMutablePointer<Agraph_t>, _ filePointer: UnsafeMutablePointer<FILE>) {
    let message = "This is a stub."

    _ = message.withCString { cString in
        fputs(cString, stderr)
    }
}

@discardableResult
func use<R>(
    fileDescriptor: Int32,
    mode: UnsafePointer<Int8>!,
    closure: (UnsafeMutablePointer<FILE>) throws -> R
) rethrows -> R {
    // Should prob remove this `!`, but IDK what a sensible recovery mechanism would be.
    let filePointer = fdopen(fileDescriptor, mode)!
    defer { fclose(filePointer) }
    return try closure(filePointer)

}

public extension UnsafeMutablePointer where Pointee == Agraph_t {
    func asString() throws -> String {
        let pipe = Pipe()

        use(fileDescriptor: pipe.fileHandleForWriting.fileDescriptor, mode: "w") { filePointer in
            agwrite(self, filePointer)
        }

        let data = pipe.fileHandleForReading.readDataToEndOfFile()

        guard let output = String(data: data, encoding: .utf8) else {
            throw AGWriteWrongEncoding()
        }  
        return output  
    }
}

let ptr = UnsafeMutablePointer<Agraph_t>.allocate(capacity: 1) // Dummy value
print(try ptr.asString())

其他几件事:

  1. 抛出错误可能是比返回更好的选择""。空字符串不是一个好的错误处理机制。返回一个可选的也可以,但无论如何它可能总是被强制解包。
  2. readDataToEndOfFile是阻塞调用,会导致不好的使用体验。最好将此代码在后台线程上运行,或者使用 aFileHandle.readabilityHandler异步使用传入的数据。

推荐阅读