如何使用文件描述符在 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。我需要做什么? 提前非常感谢!

文件描述符和文件指针不是一回事。这令人困惑,并且由于FILE *符号而很难谷歌,这一事实更加令人沮丧。

您需要fdopen文件描述符 (pipe.fileHandleForWriting.fileDescriptor(,以接收FILE *(在 Swift 中UnsafeMutablePointer<FILE>(。这就是您传递给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在数据传入时异步使用数据。