将 Swift 字符串转换为 wchar_t
Convert Swift String to wchar_t
对于上下文:我正在尝试使用非常方便的 LibXL。我在 Obj-C 和 C++ 中成功地使用了它,但现在正试图移植到 Swift。为了更好地支持 Unicode,我需要将所有字符串作为 wchar_t*
.
发送到 LibXL api
因此,为了这个目的,我拼凑了这段代码:
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
错误消息 returns '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 的回答。似乎块 'consumes' 中使用的任何指针。因此,例如,当使用
编写字符串时
("Hello".withWideChars({ wCharacters in
xlSheetWriteStrW(newSheet, destRow, destColumn, wCharacters, aFormatHandle)
})
writeStr
行执行后 aFormatHandle
将失效并且不可重复使用。有必要为每个写入命令创建一个新的 FormatHandle
。
这里有不同的问题。首先,String.cString(using:)
确实
不适用于 multi-byte 编码:
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: [=12=].value) } + [0]
return u32.withUnsafeBufferPointer { body([=12=].baseAddress!) }
}
}
然后可以像
一样使用
let name = "John Doe"
let passKey = "secret"
name.withWideChars { wname in
passKey.withWideChars { wpass in
xlBookSetKeyW(book, wname, wpass)
}
}
并且 clean-up 是自动的。
对于上下文:我正在尝试使用非常方便的 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
错误消息 returns '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 的回答。似乎块 'consumes' 中使用的任何指针。因此,例如,当使用
编写字符串时("Hello".withWideChars({ wCharacters in
xlSheetWriteStrW(newSheet, destRow, destColumn, wCharacters, aFormatHandle)
})
writeStr
行执行后 aFormatHandle
将失效并且不可重复使用。有必要为每个写入命令创建一个新的 FormatHandle
。
这里有不同的问题。首先,String.cString(using:)
确实
不适用于 multi-byte 编码:
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: [=12=].value) } + [0]
return u32.withUnsafeBufferPointer { body([=12=].baseAddress!) }
}
}
然后可以像
一样使用let name = "John Doe"
let passKey = "secret"
name.withWideChars { wname in
passKey.withWideChars { wpass in
xlBookSetKeyW(book, wname, wpass)
}
}
并且 clean-up 是自动的。