For example: var str = String(format: "%12s - %s", "key", "value")
What I want is key will hold chars of length of 12. key__________ - value
(underscore here is whitespace)
Thanks.
As documentation said, COpaquePointer is a wrapper around an opaque C pointer.
Opaque pointers are used to represent C pointers to types that cannot be represented in Swift, such as incomplete struct types.
Key is a String - native Swift type. I believe that it is better to use this Swift String function:
let testString = "bla bli blah"
testString.stringByPaddingToLength(3, withString: " ", startingAtIndex: 0)
//output = "bla"
Swift 3
let testString = "bla bli blah"
testString.padding(toLength: 3, withPad: " ", startingAt: 0)
//output = "bla"
import Foundation.Basically, to format String with String(format: _:...), we can use %@:
String(format: "%@ - %@", "key", "value")
But, I believe %@ does not support "width" modifier: you cannot %12@ or such.
So, you have to convert String to COpaquePointer which can be formatted with %s:
var key = "key"
var val = "value"
var str = String(format: "%-12s - %s",
COpaquePointer(key.cStringUsingEncoding(NSUTF8StringEncoding)!),
COpaquePointer(val.cStringUsingEncoding(NSUTF8StringEncoding)!)
)
// -> "key - value"
Swift 2 version without COpaquePointer:
import Foundation
let str = "hi".nulTerminatedUTF8
let padded = str.withUnsafeBufferPointer() {
return String(format: "%-12s", $0.baseAddress!)
}
print(padded)
Swift 3:
import Foundation
let str = "hi".utf8CString
let padded = str.withUnsafeBufferPointer() {
return String(format: "%-12s", $0.baseAddress!)
}
print(padded)
Swift 5.1 / Xcode 11.1 / iOS 13
This is really the best answer. No C string conversions (screwing up grapheme clusters), no UnsafeBufferPointer.
public extension String {
func paddedToWidth(_ width: Int) -> String {
let length = self.count
guard length < width else {
return self
}
let spaces = Array<Character>.init(repeating: " ", count: width - length)
return self + spaces
}
}
Then to use it, you can do something like this:
let fubar = "Foobar".paddedToWidth(10) + "Barfoo"
print(fubar)
// Prints "Foobar Barfoo".
Note: Strings formatted with %s cannot represent unicode characters like emojis and "ä", "ö", "ü", "ß" correctly.
There are two simple ways to solve the general problem with %s in Swift code:
Interface: String(format: String, arguments: CVarArg...)
arguments: CVarArg...let stringToFormat = "test"
let formattedString = stringToFormat.withCString{
String(format: "%s", $0)
}
If you need to use multiple strings this gets very tedious and you have to use nested closures.
Therefore...
arguments: CVarArg...The easiest way I found is making an extension to String with a computed property c
extension String {
// nested `struct` which is needed
// to keep the `baseAdress` pointer valid (see (*))
struct CString: CVarArg {
// needed to conform to `CVarArg`
var _cVarArgEncoding: [Int] = []
// needed to keep the `baseAdress` pointer valid (see (*))
var cstring: ContiguousArray<CChar> = []
init(string: String) {
// is essentially just a (special) `Array`
cstring = string.utf8CString
self._cVarArgEncoding = cstring.withUnsafeBufferPointer{
// use the `_cVarArgEncoding` of the first Buffer address (*)
$0.baseAddress!._cVarArgEncoding
}
}
}
// you only need to use this property (`c` stands for `CString`)
// e.g.: String(format: "%s", "test".c)
var c: CString {
return CString(string: self)
}
}
Usage
let stringToFormat1 = "test1"
let stringToFormat2 = "test2"
// note the `.c` at the end of each variable/literal
let formattedString = String(format: "%s %s %s", stringToFormat1.c, stringToFormat2.c, "test3".c)
Using the second solution:
// note: it should be `-12` instead of `12` in order to pad the string to the right
var str = String(format: "%-12s - %s", "key".c, "value".c)