如何在swift中使用文件描述符转移写入文件?

How to use file descriptor to divert write-to-file in swift?

我想使用一些使用文件描述符的 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。我需要做什么呢? 非常感谢!

文件描述符和文件指针are not the same thing。这令人困惑,并且由于符号 FILE * 真的很难 Google 而变得更加令人沮丧。

您需要 fdopen 文件描述符 (pipe.fileHandleForWriting.fileDescriptor),以接收 FILE *UnsafeMutablePointer<FILE> in 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是阻塞调用,会导致不好的使用体验。这段代码最好是 运行 在后台线程上,或者使用 FileHandle.readabilityHandler 异步使用传入的数据。