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