【问题标题】:How to use file descriptor to divert write-to-file in swift?如何使用文件描述符快速转移写入文件?
【发布时间】:2020-04-26 10:35:45
【问题描述】:

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

【问题讨论】:

  • 为什么是UnsafeMutablePointer&lt;Int32&gt;fopen 返回 File *
  • Swift 导入的agwrite 的确切类型签名是什么?

标签: swift macos language-interoperability


【解决方案1】:

文件描述符和文件指针are not the same thing。这很令人困惑,而且更令人沮丧的是,FILE * 因为符号而对 Google 来说真的很难。

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

【讨论】:

  • 太棒了!从我的麻烦的描述中找到解决方法。谢谢
猜你喜欢
  • 1970-01-01
  • 2010-09-20
  • 2020-04-20
  • 2019-06-20
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 1970-01-01
  • 2023-03-17
相关资源
最近更新 更多