1

对于上下文:我正在尝试使用非常方便的LibXL。我已经在 Obj-C 和 C++ 中成功使用了它,但现在正试图移植到 Swift。为了更好地支持 Unicode,我需要将所有字符串作为wchar_t*.

所以,为了这个目的,我拼凑了这段代码:

extension String {
    ///Function to convert a String into a wchar_t buffer.
    ///Don't forget to free the buffer!
    var wideChar: UnsafeMutablePointer<wchar_t>? {
        get {
            guard let _cString = self.cString(using: .utf16) else {
                return nil
            }
            let buffer = UnsafeMutablePointer<wchar_t>.allocate(capacity: _cString.count)
            memcpy(buffer, _cString, _cString.count)
            return buffer
        }
    }

对 LibXL 的调用似乎正在工作(获取print错误消息中的一条返回“Ok”)。除非我尝试实际写入测试电子表格中的单元格。我得到can't write row 0 in trial version

if let name = "John Doe".wideChar, let passKey = "mac-f.....lots of characters...3".wideChar {
            xlBookSetKeyW(book, name, passKey)
            print(">: " + String.init(cString: xlBookErrorMessageW(book)))
        }

        if let sheetName = "Output".wideChar, let path = savePath.wideChar, let test = "Hello".wideChar {
            let sheet: SheetHandle = xlBookAddSheetW(book, sheetName, nil)
            xlSheetWriteStrW(sheet, 0, 0, test, sectionTitleFormat)
            print(">: " + String.init(cString: xlBookErrorMessageW(book)))
            let success = xlBookSaveW(book, path)
            dump(success)
            print(">: " + String.init(cString: xlBookErrorMessageW(book)))
        }

我假设我的转换代码wchar_t*不正确。有人可以为我指出正确的方向吗..?

附录:感谢@MartinR 的回答。该块似乎“消耗”了其中使用的任何指针。因此,例如,当使用

("Hello".withWideChars({ wCharacters in
   xlSheetWriteStrW(newSheet, destRow, destColumn, wCharacters, aFormatHandle)
})

该行执行aFormatHandle后将变为无效writeStr并且不可重复使用。有必要FormatHandle为每个写入命令创建一个新命令。

4

1 回答 1

1

这里有不同的问题。首先,String.cString(using:)不适用于多字节编码:

print("ABC".cString(using: .utf16)!)
// [65, 0]  ???

其次,wchar_t包含UTF-32代码点,而不是UTF-16. 最后,在

let buffer = UnsafeMutablePointer<wchar_t>.allocate(capacity: _cString.count)
memcpy(buffer, _cString, _cString.count)

分配大小不包括结尾的空字符,并且副本复制的是_cString.count 字节,而不是字符。

所有这些都可以修复,但我建议使用不同的 API(类似于String.withCString(_:)方法):

extension String {
    /// Calls the given closure with a pointer to the contents of the string,
    /// represented as a null-terminated wchar_t array.
    func withWideChars<Result>(_ body: (UnsafePointer<wchar_t>) -> Result) -> Result {
        let u32 = self.unicodeScalars.map { wchar_t(bitPattern: $0.value) } + [0]
        return u32.withUnsafeBufferPointer { body($0.baseAddress!) }
    }
}

然后可以像这样使用

let name = "John Doe"
let passKey = "secret"

name.withWideChars { wname in
    passKey.withWideChars { wpass in
        xlBookSetKeyW(book, wname, wpass)
    }
}

并且清理是自动的。

于 2018-03-23T14:53:19.787 回答