Nim 0.18.0+ ships with the strformat library that provides the fmt template which allows one to format strings using format specifiers like in Python 3’s f-strings.

The strformat library provides string interpolation inspired by Python 3’s Formatted String Literals or f-strings. I find myself almost always importing strformat to get the beautiful string formatting using its fmt template.

Use the fmt template to get an interpolated string.

You can also use the unary operator & instead of fmt , which works better for the

explained in fmt and & – Printing newline using

inside fmt .

import strformat let fname = "Kaushal" lname = "Modi" echo fmt"My name is {fname} {lname}." echo & "My name is {fname} {lname}."

My name is Kaushal Modi. My name is Kaushal Modi.

The fmt can be used like a proc too:

import strformat let str = "abc" echo fmt ( "{str}" ) echo "{str}" . fmt

abc abc

Format Specifier #

The general syntax for fmt is fmt"{VARorSTRING:FORMATSPECIFIER}" (or &"{VARorSTRING:FORMATSPECIFIER}" ).

The “FORMATSPECIFIER” is further broken down into (ref):

[[fill]align][sign][#][0][minimumwidth][.precision][type]

The square brackets [] indicate an optional element.

Below sub-sections delve into the explanation of each of those format specifier fields, with examples.

[[fill] align ][sign][#][0][ minimumwidth ][.precision][type]

Table 1 fmt ‘alignment’ field ‘alignment’ field

Alignment + Min width Description N Left aligns strings, right aligns numbers (ints and floats) >N Right align, or pad spaces to the left <N Left align, or pad spaces to the right ^N Center align, or pad spaces on both sides

N is the number of characters. Spaces are padded only if N is greater than the string length.

is the number of characters. If N is not defined, the field width will always be the same size as the data to fill it. So the alignment field has no meaning in that case.

is not defined, the field width will always be the same size as the data to fill it. So the alignment field has no meaning in that case. > , < and ^ are the right, left and center alignment flags respectively.

Right align #

Number type values (ints, floats) get right aligned by default.

>N forces the right align.

import strformat let str : string = "abc" flt : float = 123.456 int1 : int = - 12345 int2 : int = - 1 int3 : int = 16 echo "String formatting:" echo & " `{str:>5}'" echo & """ `{"def":>5}'""" echo "

Float formatting:" echo fmt" `{flt:9.3f}'" echo fmt" `{flt:9.4f}'" echo fmt" `{flt:>9.3f}'" echo fmt" `{flt:>9.0f}'" echo fmt" `{flt:e}'" echo fmt" `{flt:13e}'" echo fmt" `{flt:>13e}'" echo "

Integer formatting:" echo fmt" `{int1:9}'" echo fmt" `{int1:>9}'" echo fmt" `{int2:4}'"

String formatting: ` abc' ` def' Float formatting: ` 123.456' ` 123.4560' ` 123.456' ` 123.' `1.234560e+02' ` 1.234560e+02' ` 1.234560e+02' Integer formatting: ` -12345' ` -12345' ` -1'

Left align #

String type values get left aligned by default.

<N forces the left align.

import strformat let str : string = "abc" flt : float = 123.456 int1 : int = - 12345 int2 : int = - 1 int3 : int = 16 echo "String formatting:" echo & " `{str:5}'" echo & """ `{str:<5}'""" echo & """ `{"def":<5}'""" echo "

Float formatting:" echo fmt" `{flt}'" echo fmt" `{flt:<9.4f}'" echo "

Integer formatting:" echo fmt" `{int1:<10}'" echo fmt" `{int2:<4}'" echo fmt" `{int3:<4}'"

String formatting: `abc ' `abc ' `def ' Float formatting: `123.456' `123.4560 ' Integer formatting: `-12345 ' `-1 ' `16 '

Center align #

import strformat let str : string = "abc" flt : float = 123.4 int1 : int = - 12345 echo "String formatting:" echo & " `{str:^5}'" echo & " `{str:^6}'" echo & " `{str:^7}'" echo "

Float formatting:" echo fmt" `{flt:^11.3f}'" echo fmt" `{flt:^11.0f}'" echo fmt" `{flt:^13e}'" echo fmt" `{flt:^11.4f}'" echo "

Integer formatting:" echo fmt" `{int1:^10}'"

String formatting: ` abc ' ` abc ' ` abc ' Float formatting: ` 123.400 ' ` 123. ' `1.234000e+02 ' ` 123.4000 ' Integer formatting: ` -12345 '

If exact centering cannot be done, the one extra space is added to the right.

[[ fill ] align ][sign][#][0][ minimumwidth ][.precision][type]

Table 2 fmt ‘fill’ field ‘fill’ field

Fill + Alignment + Min width Description F>N Right align and pad the “F” char to the left F<N Left align and pad the “F” char to the right F^N Center align and pad the “F” char on both sides

N is the number of characters. Filling happens only if N is greater than the string length.

is the number of characters. F is the optional ‘fill’ character. The fill character, if present, must be followed by an alignment flag ( < , > , ^ ).

is the optional ‘fill’ character.

Right Align + Fill #

import strformat let str : string = "abc" flt : float = 123.456 int1 : int = 123 echo "String formatting:" echo & " `{str:x>5}'" echo "

Float formatting:" echo fmt" `{flt:x>9.3f}'" echo "

Integer formatting:" echo fmt" `{int1:x>9}'"

String formatting: `xxabc' Float formatting: `xx123.456' Integer formatting: `xxxxxx123'

Left Align + Fill #

import strformat let str : string = "abc" flt : float = 123.456 int1 : int = 123 echo "String formatting:" echo & " `{str:x<5}'" echo "

Float formatting:" echo fmt" `{flt:x<9.3f}'" echo "

Integer formatting:" echo fmt" `{int1:x<9}'"

String formatting: `abcxx' Float formatting: `123.456xx' Integer formatting: `123xxxxxx'

Center Align + Fill #

import strformat let str : string = "abc" flt : float = 123.456 int1 : int = 123 echo "String formatting:" echo & " `{str:x^5}'" echo "

Float formatting:" echo fmt" `{flt:x^9.3f}'" echo "

Integer formatting:" echo fmt" `{int1:x^9}'"

String formatting: `xabcx' Float formatting: `x123.456x' Integer formatting: `xxx123xxx'

[[fill]align][ sign ][#][0][minimumwidth][.precision][type]

The ‘sign’ field is only valid for numeric types, and can be one of the following:

Table 3 fmt ‘sign’ field ‘sign’ field

Sign Meaning - (default) Indicates that a sign should be used only for negative numbers. + Indicates that a sign should be used for both positive as well as negative numbers. (space) Indicates that a leading space should be used on positive numbers.

The ‘sign’ field affects the formatting of only positive numbers.

import strformat let intp : int = 32 intn : int = - 44 fltp : float = 1.123 fltn : float = - 4.56 echo "Positive integer formatting:" echo fmt" Default: `{intp}'" echo fmt" -: `{intp:-}'" echo fmt" +: `{intp:+}'" echo fmt" space: `{intp: }'" echo "Negative integer formatting:" echo fmt" Default: `{intn}'" echo fmt" -: `{intn:-}'" echo fmt" +: `{intn:+}'" echo fmt" space: `{intn: }'" echo "

Positive float formatting:" echo fmt" Default: `{fltp}'" echo fmt" -: `{fltp:-}'" echo fmt" +: `{fltp:+}'" echo fmt" space: `{fltp: }'" echo "Negative float formatting:" echo fmt" Default: `{fltn}'" echo fmt" -: `{fltn:-}'" echo fmt" +: `{fltn:+}'" echo fmt" space: `{fltn: }'"

Positive integer formatting: Default: `32' -: `32' +: `+32' space: ` 32' Negative integer formatting: Default: `-44' -: `-44' +: `-44' space: `-44' Positive float formatting: Default: `1.123' -: `1.123' +: `+1.123' space: ` 1.123' Negative float formatting: Default: `-4.56' -: `-4.56' +: `-4.56' space: `-4.56'

FIXED Negative zero #

import strformat echo fmt"{-0.0:+}"

-0

Older issue #

Nim Issue #7923

Thanks to @skilchen from GitHub for fixing this in 230692a22f!

Hash sign “#” (only for integers) #

[[fill]align][sign][ # ][0][minimumwidth][.precision][ type ]

If the ‘#’ character is present, integers use an alternate form for formatting. This means that binary, octal, and hexadecimal output will be prefixed with ‘0b’, ‘0o’, and ‘0x’, respectively.

The presentation type for the integer is specified in the ‘type’ field.

Table 4 fmt ‘type’ field for integers ‘type’ field for integers

Type Description d (or none) (default) Decimal Integer. Outputs the number in base 10. b Binary. Outputs the number in base 2. o Octal format. Outputs the number in base 8. x Hex format. Outputs the number in base 16, using lower-case letters for the digits above 9. X Hex format. Outputs the number in base 16, using uppercase letters for the digits above 9.

If the ‘type’ field is not specified, “#” does not do anything (it defaults to the decimal presentation).

import strformat let i_seq : seq [ int ] = @[- 16 , 0 , 57005 , 48879 ] for i in i_seq : echo fmt"value = {i}" echo fmt" none: `{i}'" echo fmt" d: `{i:#d}'" echo fmt" b: `{i:#b}'" echo fmt" o: `{i:#o}'" echo fmt" x: `{i:#x}'" echo fmt" X: `{i:#X}'"

value = -16 none: `-16' d: `-16' b: `-0b10000' o: `-0o20' x: `-0x10' X: `-0x10' value = 0 none: `0' d: `0' b: `0b0' o: `0o0' x: `0x0' X: `0x0' value = 57005 none: `57005' d: `57005' b: `0b1101111010101101' o: `0o157255' x: `0xdead' X: `0xDEAD' value = 48879 none: `48879' d: `48879' b: `0b1011111011101111' o: `0o137357' x: `0xbeef' X: `0xBEEF'

[[fill]align][sign][#][ 0 ][minimumwidth][.precision][type]

Zero-padding is enabled if the ‘minimumwidth’ field is preceded by a zero (‘0’) character.

import strformat let int1 : int = - 12345 int2 : int = 1 echo fmt"`{int1:08}'" echo fmt"`{int2:04}'"

`-0012345' `0001'

import strformat echo fmt"{-1.5:08}" echo fmt"{1.5:08}"

-00001.5 000001.5

Older issue #

Thanks to @skilchen from GitHub, this issue was fixed in 07ff9940f4.

This was Nim Issue #7932 – Zero padding did not work with floats.

[[fill]align][sign][#][0][minimumwidth][ .precision ][type]

For floats The ‘precision’ is a decimal number indicating how many digits should be displayed after the decimal point in a floating point conversion. For integers This field is ignored. For non-numeric types This field indicates the maximum field size - in other words, how many characters will be used from the field content.

import strformat echo fmt" .1 :: {12.345678:.1}" echo fmt" 7.1 :: {12.345678:7.1}" echo fmt" 7.1g :: {12.345678:7.1g}" echo fmt" 7.1f :: {12.345678:7.1f}" echo fmt" 07.1f :: {12.345678:07.1f}" echo fmt"+07.1f :: {12.345678:+07.1f}" echo fmt"+07.2f :: {12.345678:+07.2f}" echo fmt"+07.3f :: {12.345678:+07.3f}" echo fmt"+07.4f :: {12.345678:+07.4f}" echo fmt"+07.5f :: {12.345678:+07.5f}" echo fmt"+07.6f :: {12.345678:+07.6f}"

.1 :: 1.e+01 7.1 :: 1.e+01 7.1g :: 1.e+01 7.1f :: 12.3 07.1f :: 00012.3 +07.1f :: +0012.3 +07.2f :: +012.35 +07.3f :: +12.346 +07.4f :: +12.3457 +07.5f :: +12.34568 +07.6f :: +12.345678

FIXED Strings #

import strformat let str = "abc" echo fmt">7.1 :: `{str:>7.1}'" echo fmt" 7.1 :: `{str:7.1}'" echo fmt" 7.2 :: `{str:7.2}'" echo fmt" 7.3 :: `{str:7.3}'"

>7.1 :: ` a' 7.1 :: `a ' 7.2 :: `ab ' 7.3 :: `abc '

Older issue #

Nim Issue #7933 – The .precision field should behave as maxwidth for strings as per the documentation, but did not work.

Thanks to @skilchen from GitHub for fixing this in fd102f39bb!

Type (only for numbers) #

[[fill]align][sign][#][0][minimumwidth][.precision][ type ]

See Hash sign “#” (only for integers).

Table 5 fmt ‘type’ field for floats ‘type’ field for floats

Type Description e Exponent notation. Prints the number in scientific notation using the letter ‘e’ to indicate the exponent. E Exponent notation. Same as ‘e’ except it converts the number to uppercase. f Fixed point. Displays the number as a fixed-point number. F Fixed point. Same as ‘f’ except it converts the number to uppercase. g General format. This prints the number as a fixed-point number, unless the number is too large, in which case it switches to ‘e’ exponent notation. G General format. Same as ‘g’ except switches to ‘E’ if the number gets to large. (None) (default) similar to ‘g’, except that it prints at least one digit after the decimal point.

import strformat let f_seq : seq [ float ] = @[- 1234.567 , 0.0 , 1.2345 , 5678901234.5678 ] for flt in f_seq : echo fmt"value = {flt}" echo fmt" none: `{flt}'" echo fmt" e: `{flt:e}'" echo fmt" E: `{flt:E}'" echo fmt" f: `{flt:f}'" echo fmt" F: `{flt:F}'" echo fmt" g: `{flt:g}'" echo fmt" G: `{flt:G}'"

value = -1234.567 none: `-1234.567' e: `-1.234567e+03' E: `-1.234567E+03' f: `-1234.567000' F: `-1234.567000' g: `-1234.57' G: `-1234.57' value = 0.0 none: `0.0' e: `0.000000e+00' E: `0.000000E+00' f: `0.000000' F: `0.000000' g: `0' G: `0' value = 1.2345 none: `1.2345' e: `1.234500e+00' E: `1.234500E+00' f: `1.234500' F: `1.234500' g: `1.2345' G: `1.2345' value = 5678901234.5678 none: `5678901234.5678' e: `5.678901e+09' E: `5.678901E+09' f: `5678901234.567800' F: `5678901234.567800' g: `5.6789e+09' G: `5.6789E+09'

For strings, the ‘type’ field is implicitly ’s’, the only valid value for strings.

import strformat let str = "abc" echo fmt"{str}" echo fmt"{str:s}"

abc abc

fmt Limitations #

fmt and & – Printing newline using

inside fmt #

As you see above, you can use either fmt or the unary & operator for formatting. The difference between them is subtle but important.

The fmt"{expr}" syntax is more aesthetically pleasing, but it hides a small gotcha — The string is a generalized raw string literal. So the below won’t work:

import strformat echo fmt"line1

line2"

line1

line2

But there are multiple ways to get around that limitation:

Use & instead import strformat echo & "line1

line2" line1 line2 Wrap the

in braces import strformat echo fmt"line1{'

'}line2" line1 line2 Call fmt like a proc import strformat echo fmt ( "line1

line2" ) echo "line1

line2" . fmt line1 line2 line1 line2

TO BE FIXED fmt and & formatting strings cannot be a variable #

import strformat let str = "abc" f = "{abc}" echo fmt ( f )

Trying to evaluate the above gives this error:

stack trace: (most recent call last) lib/pure/strformat.nim(281) & nim_src_rJX8ZE.nim(8, 10) Error: string formatting (fmt(), &) only works with string literals

It’s the same for & too; same the same error:

import strformat let str = "abc" f = "{abc}" echo & f

FIXED fmt does not handle open arrays #

Nim Issue #7940

Thanks to @data-man from GitHub, this issue is now fixed!

import strformat proc foo ( x : openArray [ int ] ) = echo fmt"{x}" foo ( [ 1 , 2 , 3 ] )

[1, 2, 3]

See this section in Nim notes for details about the failure before this fix.