Precision String Format Specifier In Swift
My best solution so far, following from David's response:
import Foundation
extension Int {
func format(f: String) -> String {
return String(format: "%\(f)d", self)
}
}
extension Double {
func format(f: String) -> String {
return String(format: "%\(f)f", self)
}
}
let someInt = 4, someIntFormat = "03"
println("The integer number \(someInt) formatted with \"\(someIntFormat)\" looks like \(someInt.format(someIntFormat))")
// The integer number 4 formatted with "03" looks like 004
let someDouble = 3.14159265359, someDoubleFormat = ".3"
println("The floating point number \(someDouble) formatted with \"\(someDoubleFormat)\" looks like \(someDouble.format(someDoubleFormat))")
// The floating point number 3.14159265359 formatted with ".3" looks like 3.142
I think this is the most Swift-like solution, tying the formatting operations directly to the data type. It may well be that there is a built-in library of formatting operations somewhere, or maybe it will be released soon. Keep in mind that the language is still in beta.
What are the supported Swift String format specifiers?
The format specifiers for String
formatting in Swift are the same as those in Objective-C NSString
format, itself identical to those for CFString
format and are buried deep in the archives of Apple Documentation (same content for both pages, both originally from year 2002 or older):
- https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFStrings/formatSpecifiers.html
- https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html
But this documentation page itself is incomplete, for instance the flags, the precision specifiers and the width specifiers aren't mentioned. Actually, it claims to follow IEEE printf specifications (Issue 6, 2004 Edition), itself aligned with the ISO C standard. So those specifiers should be identical to what we have with C printf
, with the addition of the %@
specifier for Objective-C objects, and the addition of the poorly documented %D
, %U
, %O
specifiers and q
length modifier.
Specifiers
Each conversion specification is introduced by the '%' character or by the character sequence "%n$".
n
is the index of the parameter, like in:
String(format: "%2$@ %1$@", "world", "Hello")
Format Specifiers
%@ Objective-C object, printed as the string returned by descriptionWithLocale: if available, or description otherwise.
Actually, you may also use some Swift types, but they must be defined inside the standard library in order to conform to the CVarArg protocol, and I believe they need to support bridging to Objective-C objects: https://developer.apple.com/documentation/foundation/object_runtime/classes_bridged_to_swift_standard_library_value_types.
String(format: "%@", ["Hello", "world"])
%% '%' character.
String(format: "100%% %@", true.description)
%d, %i Signed 32-bit integer (int).
String(format: "from %d to %d", Int32.min, Int32.max)
%u, %U, %D Unsigned 32-bit integer (unsigned int).
String(format: "from %u to %u", UInt32.min, UInt32.max)
%x Unsigned 32-bit integer (unsigned int), printed in hexadecimal using the digits 0–9 and lowercase a–f.
String(format: "from %x to %x", UInt32.min, UInt32.max)
%X Unsigned 32-bit integer (unsigned int), printed in hexadecimal using the digits 0–9 and uppercase A–F.
String(format: "from %X to %X", UInt32.min, UInt32.max)
%o, %O Unsigned 32-bit integer (unsigned int), printed in octal.
String(format: "from %o to %o", UInt32.min, UInt32.max)
%f 64-bit floating-point number (double), printed in decimal notation. Produces "inf", "infinity", or "nan".
String(format: "from %f to %f", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%F 64-bit floating-point number (double), printed in decimal notation. Produces "INF", "INFINITY", or "NAN".
String(format: "from %F to %F", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%e 64-bit floating-point number (double), printed in scientific notation using a lowercase e to introduce the exponent.
String(format: "from %e to %e", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%E 64-bit floating-point number (double), printed in scientific notation using an uppercase E to introduce the exponent.
String(format: "from %E to %E", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%g 64-bit floating-point number (double), printed in the style of %e if the exponent is less than –4 or greater than or equal to the precision, in the style of %f otherwise.
String(format: "from %g to %g", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%G 64-bit floating-point number (double), printed in the style of %E if the exponent is less than –4 or greater than or equal to the precision, in the style of %f otherwise.
String(format: "from %G to %G", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%c 8-bit unsigned character (unsigned char).
String(format: "from %c to %c", "a".utf8.first!, "z".utf8.first!)
%C 16-bit UTF-16 code unit (unichar).
String(format: "from %C to %C", "爱".utf16.first!, "终".utf16.first!)
%s Null-terminated array of 8-bit unsigned characters.
"Hello world".withCString {
String(format: "%s", $0)
}
%S Null-terminated array of 16-bit UTF-16 code units.
"Hello world".withCString(encodedAs: UTF16.self) {
String(format: "%S", $0)
}
%p Void pointer (void *), printed in hexadecimal with the digits 0–9 and lowercase a–f, with a leading 0x.
var hello = "world"
withUnsafePointer(to: &hello) {
String(format: "%p", $0)
}
%n The argument shall be a pointer to an integer into which is written the number of bytes written to the output so far by this call to one of the fprintf() functions.
The n
format specifier seems unsupported in Swift 4+
%a 64-bit floating-point number (double), printed in scientific notation with a leading 0x and one hexadecimal digit before the decimal point using a lowercase p to introduce the exponent.
String(format: "from %a to %a", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
%A 64-bit floating-point number (double), printed in scientific notation with a leading 0X and one hexadecimal digit before the decimal point using a uppercase P to introduce the exponent.
String(format: "from %A to %A", Double.leastNonzeroMagnitude, Double.greatestFiniteMagnitude)
Flags
' The integer portion of the result of a
decimal conversion ( %i, %d, %u, %f, %F, %g, or %G ) shall be
formatted with thousands' grouping characters. For other conversions
the behavior is undefined. The non-monetary grouping character is
used.
The '
flag seems unsupported in Swift 4+
- The result of the conversion shall be left-justified within the field. The conversion is right-justified if
this flag is not specified.
String(format: "from %-12f to %-12d.", Double.leastNonzeroMagnitude, Int32.max)
+ The result of a signed conversion shall always begin with a sign ( '+' or '-' ). The conversion shall begin
with a sign only when a negative value is converted if this flag is
not specified.
String(format: "from %+f to %+d", Double.leastNonzeroMagnitude, Int32.max)
<space> If the first character of a signed
conversion is not a sign or if a signed conversion results in no
characters, a <space> shall be prefixed to the result. This means
that if the <space> and '+' flags both appear, the <space> flag
shall be ignored.
String(format: "from % d to % d.", Int32.min, Int32.max)
# Specifies that the value is to be converted to an alternative form. For o conversion, it increases the precision
(if necessary) to force the first digit of the result to be zero. For
x or X conversion specifiers, a non-zero result shall have 0x (or 0X)
prefixed to it. For a, A, e, E, f, F, g , and G conversion specifiers,
the result shall always contain a radix character, even if no digits
follow the radix character. Without this flag, a radix character
appears in the result of these conversions only if a digit follows it.
For g and G conversion specifiers, trailing zeros shall not be removed
from the result as they normally are. For other conversion specifiers,
the behavior is undefined.
String(format: "from %#a to %#x.", Double.leastNonzeroMagnitude, UInt32.max)
0 For d, i, o, u, x, X, a, A, e, E, f, F, g,
and G conversion specifiers, leading zeros (following any indication
of sign or base) are used to pad to the field width; no space padding
is performed. If the '0' and '-' flags both appear, the '0' flag is
ignored. For d, i, o, u, x, and X conversion specifiers, if a
precision is specified, the '0' flag is ignored. If the '0' and '"
flags both appear, the grouping characters are inserted before zero
padding. For other conversions, the behavior is undefined.
String(format: "from %012f to %012d.", Double.leastNonzeroMagnitude, Int32.max)
Width modifiers
If the converted value has fewer bytes than the field width, it shall be padded with spaces by default on the left; it shall be padded on the right if the left-adjustment flag ( '-' ) is given to the field width. The field width takes the form of an asterisk ( '*' ) or a decimal integer.
String(format: "from %12f to %*d.", Double.leastNonzeroMagnitude, 12, Int32.max)
Precision modifiers
An optional precision that gives the minimum number of digits to appear for the d, i, o, u, x, and X conversion specifiers; the number of digits to appear after the radix character for the a, A, e, E, f, and F conversion specifiers; the maximum number of significant digits for the g and G conversion specifiers; or the maximum number of bytes to be printed from a string in the s and S conversion specifiers. The precision takes the form of a period ( '.' ) followed either by an asterisk ( '*' ) or an optional decimal digit string, where a null digit string is treated as zero. If a precision appears with any other conversion specifier, the behavior is undefined.
String(format: "from %.12f to %.*d.", Double.leastNonzeroMagnitude, 12, Int32.max)
Length modifiers
h Length modifier specifying that a following
d, o, u, x, or X conversion specifier applies to a short or unsigned
short argument.
String(format: "from %hd to %hu", CShort.min, CUnsignedShort.max)
hh Length modifier specifying that a following
d, o, u, x, or X conversion specifier applies to a signed char or
unsigned char argument.
String(format: "from %hhd to %hhu", CChar.min, CUnsignedChar.max)
l Length modifier specifying that a following
d, o, u, x, or X conversion specifier applies to a long or unsigned
long argument.
String(format: "from %ld to %lu", CLong.min, CUnsignedLong.max)
ll, q Length modifiers specifying that a
following d, o, u, x, or X conversion specifier applies to a long long
or unsigned long long argument.
String(format: "from %lld to %llu", CLongLong.min, CUnsignedLongLong.max)
L Length modifier specifying that a following
a, A, e, E, f, F, g, or G conversion specifier applies to a long
double argument.
I wasn't able to pass a CLongDouble argument to format
in Swift 4+
z Length modifier specifying that a following
d, o, u, x, or X conversion specifier applies to a size_t.
String(format: "from %zd to %zu", size_t.min, size_t.max)
t Length modifier specifying that a following
d, o, u, x, or X conversion specifier applies to a ptrdiff_t.
String(format: "from %td to %tu", ptrdiff_t.min, ptrdiff_t.max)
j Length modifier specifying that a following
d, o, u, x, or X conversion specifier applies to a intmax_t or
uintmax_t argument.
String(format: "from %jd to %ju", intmax_t.min, uintmax_t.max)
String formatting of a Double
There's no way to do this using String
interpolation.
You can find a good discussion about this on this Apple Dev Forum post.
You can use NSNumberFormatter
or NSDateFormatter
to format the output.
Alternatively, to use printf
-like syntax, you can still use NSString
:
var str = NSString(format: "Hello, world %.2f", 42.12312312313)
Precision String Format Specifier In Swift
My best solution so far, following from David's response:
import Foundation
extension Int {
func format(f: String) -> String {
return String(format: "%\(f)d", self)
}
}
extension Double {
func format(f: String) -> String {
return String(format: "%\(f)f", self)
}
}
let someInt = 4, someIntFormat = "03"
println("The integer number \(someInt) formatted with \"\(someIntFormat)\" looks like \(someInt.format(someIntFormat))")
// The integer number 4 formatted with "03" looks like 004
let someDouble = 3.14159265359, someDoubleFormat = ".3"
println("The floating point number \(someDouble) formatted with \"\(someDoubleFormat)\" looks like \(someDouble.format(someDoubleFormat))")
// The floating point number 3.14159265359 formatted with ".3" looks like 3.142
I think this is the most Swift-like solution, tying the formatting operations directly to the data type. It may well be that there is a built-in library of formatting operations somewhere, or maybe it will be released soon. Keep in mind that the language is still in beta.
Swift custom string format extension always return 0
I think it happens because return type of the format function in extension is (_ format: String, _ arguments: CVarArg...)
. return must be String(format: String, arguments:[CVarArg])
.Parameter arguments
in function is type of [CVarArg]
and If you use _ arguments: CVarArg...
instead of [CVarArg]
in return String format type , the parameter of the arguments
gonna be [[CVarArg]]
. It is actually 2d array right now . It may fail because of this.
Also this is not works too
print(String(format: "%.1f", [1.12])) // args is CVarArg...
String format for Double type number
You have to declare a NumberFormatter() and add all the functionalities you want. For this specific task, this solves it:
import Foundation
// import Foundation.NSFormatter
let formatter = NumberFormatter()
let myNumber: Double = 25250450
formatter.numberStyle = .decimal
formatter.decimalSeparator = ","
formatter.groupingSeparator="."
formatter.minimumFractionDigits = 2
//you can add other parameters to formatter such as
//:-> formatter.maximumFractionDigits
let formattedString = formatter.string(from: NSNumber(value: myNumber))!
print(formattedString)
Full documentation of the NumberFormatter
class is available at https://developer.apple.com/documentation/foundation/numberformatter
EDIT (SwiftUI variant):
import SwiftUI
import Foundation
struct ContentView: View {
func parseNumber(_ myNumber:Double)->String?{
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.decimalSeparator = ","
formatter.groupingSeparator="."
formatter.minimumFractionDigits = 2
//you can add other parameters to formatter such as
//:-> formatter.maximumFractionDigits
let formattedString = formatter.string(from: NSNumber(value: myNumber))!
return formattedString
}
var body: some View {
let myNumber: Double = 25250450
Text("\(parseNumber(myNumber)!)")
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
What is the relevance of this String format specifier?
%@
is something similar to %d
or anything like that. This is the way of string interpolation in Swift.
To be exact %@
is placeholder for object - used in Objective-C A LOT. Since NSString *
was object (now it is only String), it was used to insert NSString *
into another NSString *
.
Also given code is just rewritten objective-c code which was something like
NSString *tempPath = [NSString stringWithFormat:@"%@/%@", path, filename];
which can be rewritten in swift:
let tempPath = path + "/" + fileName
Also, given path = "Test" and fileName = "great" will give output Test/great.
One more note: %@ is as good as dangerous. You can put UITableView as well as String in it. It will use description property for inserting into string.
Swift lose precision in decimal formatting
Using the new FormatStyle seems to generate the correct result
let format = Decimal.FormatStyle
.number
.precision(.fractionLength(0...2))
let text = "89806.9"
let value = try! format.parseStrategy.parse(text)
Below is an example parsing a currency using the currency code from the locale
let currencyFormat = Decimal.FormatStyle.Currency
.currency(code: Locale.current.currencyCode!)
.precision(.fractionLength(0...2))
let amount = try! currencyFormat.parseStrategy.parse(text)
Swedish example:
let text = "89806,9 kr"
print(amount)
89806.9
Another option is to use the new init for Decimal that takes a String and a FormatStyle.Currency (or a Number or Percent)
let amount = try Decimal(text, format: currencyFormat)
and to format this value we can use formatted(_:)
on Decimal
print(amount.formatted(currencyFormat))
Output (still Swedish):
89 806,9 kr
Related Topics
Difference Between 'Let' and 'Var' in Swift
String Value to Unsafepointer≪Uint8≫ Function Parameter Behavior
How to Unwrap an Optional Value from Any Type
Xcode Swift Am/Pm Time to 24 Hour Format
Arkit - What Do the Different Columns in Transform Matrix Represent
What Does the Dollar Sign Do in Swift/Swiftui
Property Initializers Run Before 'Self' Is Available
How to Get Mathemical Pi Constant in Swift
Knowing Where Retain Cycles Are and Removing Them
Swift - Sort Array of Objects With Multiple Criteria
Uitableview With More Than One Custom Cells With Swift
What Are the New "For", "At", "In" Keywords in Swift3 Function Declarations
Initialize @Stateobject With a Parameter in Swiftui
Getting Keyboard Size from Userinfo in Swift
Decrement Index in a Loop After Swift C-Style Loops Deprecated