我想使用一些使用文件描述符的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())
其他几件事:
- 抛出错误可能是比返回
""
更好的选择。空字符串不是一个很好的错误处理机制。返回可选选项也可以,但无论如何,它可能总是被强制解开包装。 readDataToEndOfFile
是阻止调用,可能会导致糟糕的使用体验。最好在后台线程上运行此代码,或者使用FileHandle.readabilityHandler
在数据传入时异步使用数据。