1 // Written in the D programming language.
2 
3 /**
4 This package provides string formatting functionality using
5 `printf` style format strings.
6 
7 $(BOOKTABLE ,
8 $(TR $(TH Submodule) $(TH Function Name) $(TH Description))
9 $(TR
10     $(TD $(I package))
11     $(TD $(LREF format))
12     $(TD Converts its arguments according to a format string into a string.)
13 )
14 $(TR
15     $(TD $(I package))
16     $(TD $(LREF sformat))
17     $(TD Converts its arguments according to a format string into a buffer.)
18 )
19 $(TR
20     $(TD $(I package))
21     $(TD $(LREF FormatException))
22     $(TD Signals a problem while formatting.)
23 )
24 $(TR
25     $(TD $(MREF_ALTTEXT $(D write), std, format, write))
26     $(TD $(REF_ALTTEXT $(D formattedWrite), formattedWrite, std, format, write))
27     $(TD Converts its arguments according to a format string and writes
28          the result to an output range.)
29 )
30 $(TR
31     $(TD $(MREF_ALTTEXT $(D write), std, format, write))
32     $(TD $(REF_ALTTEXT $(D formatValue), formatValue, std, format, write))
33     $(TD Formats a value of any type according to a format specifier and
34          writes the result to an output range.)
35 )
36 $(TR
37     $(TD $(MREF_ALTTEXT $(D read), std, format, read))
38     $(TD $(REF_ALTTEXT $(D formattedRead), formattedRead, std, format, read))
39     $(TD Reads an input range according to a format string and stores the read
40          values into its arguments.)
41 )
42 $(TR
43     $(TD $(MREF_ALTTEXT $(D read), std, format, read))
44     $(TD $(REF_ALTTEXT $(D unformatValue), unformatValue, std, format, read))
45     $(TD Reads a value from the given input range and converts it according to
46          a format specifier.)
47 )
48 $(TR
49     $(TD $(MREF_ALTTEXT $(D spec), std, format, spec))
50     $(TD $(REF_ALTTEXT $(D FormatSpec), FormatSpec, std, format, spec))
51     $(TD A general handler for format strings.)
52 )
53 $(TR
54     $(TD $(MREF_ALTTEXT $(D spec), std, format, spec))
55     $(TD $(REF_ALTTEXT $(D singleSpec), singleSpec, std, format, spec))
56     $(TD Helper function that returns a `FormatSpec` for a single format specifier.)
57 ))
58 
59 Limitation: This package does not support localization, but
60     adheres to the rounding mode of the floating point unit, if
61     available.
62 
63 $(SECTION3 Format Strings)
64 
65 The functions contained in this package use $(I format strings). A
66 format string describes the layout of another string for reading or
67 writing purposes. A format string is composed of normal text
68 interspersed with $(I format specifiers). A format specifier starts
69 with a percentage sign $(B '%'), optionally followed by one or more
70 $(I parameters) and ends with a $(I format indicator). A format
71 indicator may be a simple $(I format character) or a $(I compound
72 indicator).
73 
74 $(I Format strings) are composed according to the following grammar:
75 
76 $(PRE
77 $(I FormatString):
78     $(I FormatStringItem) $(I FormatString)
79 $(I FormatStringItem):
80     $(I Character)
81     $(I FormatSpecifier)
82 $(I FormatSpecifier):
83     $(B '%') $(I Parameters) $(I FormatIndicator)
84 
85 $(I FormatIndicator):
86     $(I FormatCharacter)
87     $(I CompoundIndicator)
88 $(I FormatCharacter):
89     $(I see remark below)
90 $(I CompoundIndicator):
91     $(B '$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)')
92     $(B '$(LPAREN)') $(I FormatString) $(B '%|') $(I Delimiter) $(B '%$(RPAREN)')
93 $(I Delimiter)
94     $(I empty)
95     $(I Character) $(I Delimiter)
96 
97 $(I Parameters):
98     $(I Position) $(I Flags) $(I Width) $(I Precision) $(I Separator)
99 $(I Position):
100     $(I empty)
101     $(I Integer) $(B '$')
102     $(I Integer) $(B ':') $(I Integer) $(B '$')
103     $(I Integer) $(B ':') $(B '$')
104 $(I Flags):
105     $(I empty)
106     $(I Flag) $(I Flags)
107 $(I Flag):
108     $(B '-')|$(B '+')|$(B ' ')|$(B '0')|$(B '#')|$(B '=')
109 $(I Width):
110     $(I OptionalPositionalInteger)
111 $(I Precision):
112     $(I empty)
113     $(B '.') $(I OptionalPositionalInteger)
114 $(I Separator):
115     $(I empty)
116     $(B ',') $(I OptionalInteger)
117     $(B ',') $(I OptionalInteger) $(B '?')
118 $(I OptionalInteger):
119     $(I empty)
120     $(I Integer)
121     $(B '*')
122 $(I OptionalPositionalInteger):
123     $(I OptionalInteger)
124     $(B '*') $(I Integer) $(B '$')
125 
126 $(I Character)
127     $(B '%%')
128     $(I AnyCharacterExceptPercent)
129 $(I Integer):
130     $(I NonZeroDigit) $(I Digits)
131 $(I Digits):
132     $(I empty)
133     $(I Digit) $(I Digits)
134 $(I NonZeroDigit):
135     $(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
136 $(I Digit):
137     $(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
138 )
139 
140 Note: $(I FormatCharacter) is unspecified. It can be any character
141 that has no other purpose in this grammar, but it is
142 recommended to assign (lower- and uppercase) letters.
143 
144 Note: The $(I Parameters) of a $(I CompoundIndicator) are currently
145 limited to a $(B '-') flag.
146 
147 $(SECTION4 Format Indicator)
148 
149 The $(I format indicator) can either be a single character or an
150 expression surrounded by $(B %\() and $(B %\)). It specifies the
151 basic manner in which a value will be formatted and is the minimum
152 requirement to format a value.
153 
154 The following characters can be used as $(I format characters):
155 
156 $(BOOKTABLE ,
157    $(TR $(TH FormatCharacter) $(TH Semantics))
158    $(TR $(TD $(B 's'))
159         $(TD To be formatted in a human readable format.
160              Can be used with all types.))
161    $(TR $(TD $(B 'c'))
162         $(TD To be formatted as a character.))
163    $(TR $(TD $(B 'd'))
164         $(TD To be formatted as a signed decimal integer.))
165    $(TR $(TD $(B 'u'))
166         $(TD To be formatted as a decimal image of the underlying bit representation.))
167    $(TR $(TD $(B 'b'))
168         $(TD To be formatted as a binary image of the underlying bit representation.))
169    $(TR $(TD $(B 'o'))
170         $(TD To be formatted as an octal image of the underlying bit representation.))
171    $(TR $(TD $(B 'x') / $(B 'X'))
172         $(TD To be formatted as a hexadecimal image of the underlying bit representation.))
173    $(TR $(TD $(B 'e') / $(B 'E'))
174         $(TD To be formatted as a real number in decimal scientific notation.))
175    $(TR $(TD $(B 'f') / $(B 'F'))
176         $(TD To be formatted as a real number in decimal natural notation.))
177    $(TR $(TD $(B 'g') / $(B 'G'))
178         $(TD To be formatted as a real number in decimal short notation.
179              Depending on the number, a scientific notation or
180              a natural notation is used.))
181    $(TR $(TD $(B 'a') / $(B 'A'))
182         $(TD To be formatted as a real number in hexadecimal scientific notation.))
183    $(TR $(TD $(B 'r'))
184         $(TD To be formatted as raw bytes.
185              The output may not be printable and depends on endianness.))
186 )
187 
188 The $(I compound indicator) can be used to describe compound types
189 like arrays or structs in more detail. A compound type is enclosed
190 within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is
191 applied to individual elements. The trailing portion of the
192 sub-format string following the specifier for the element is
193 interpreted as the delimiter, and is therefore omitted following the
194 last element. The $(B '%|') specifier may be used to explicitly
195 indicate the start of the delimiter, so that the preceding portion of
196 the string will be included following the last element.
197 
198 The $(I format string) inside of the $(I compound indicator) should
199 contain exactly one $(I format specifier) (two in case of associative
200 arrays), which specifies the formatting mode of the elements of the
201 compound type. This $(I format specifier) can be a $(I compound
202 indicator) itself.
203 
204 Note: Inside a $(I compound indicator), strings and characters are
205 escaped automatically. To avoid this behavior, use `"%-$(LPAREN)"`
206 instead of `"%$(LPAREN)"`.
207 
208 $(SECTION4 Flags)
209 
210 There are several flags that affect the outcome of the formatting.
211 
212 $(BOOKTABLE ,
213    $(TR $(TH Flag) $(TH Semantics))
214    $(TR $(TD $(B '-'))
215         $(TD When the formatted result is shorter than the value
216              given by the width parameter, the output is left
217              justified. Without the $(B '-') flag, the output remains
218              right justified.
219 
220              There are two exceptions where the $(B '-') flag has a
221              different meaning: (1) with $(B 'r') it denotes to use little
222              endian and (2) in case of a compound indicator it means that
223              no special handling of the members is applied.))
224    $(TR $(TD $(B '='))
225         $(TD When the formatted result is shorter than the value
226              given by the width parameter, the output is centered.
227              If the central position is not possible it is moved slightly
228              to the right. In this case, if $(B '-') flag is present in
229              addition to the $(B '=') flag, it is moved slightly to the left.))
230    $(TR $(TD $(B '+') / $(B ' '))
231         $(TD Applies to numerical values. By default, positive numbers are not
232              formatted to include the `+` sign. With one of these two flags present,
233              positive numbers are preceded by a plus sign or a space.
234              When both flags are present, a plus sign is used.
235 
236              In case of $(B 'r'), a big endian format is used.))
237    $(TR $(TD $(B '0'))
238         $(TD Is applied to numerical values that are printed right justified.
239              If the zero flag is present, the space left to the number is
240              filled with zeros instead of spaces.))
241    $(TR $(TD $(B '#'))
242         $(TD Denotes that an alternative output must be used. This depends on the type
243              to be formatted and the $(I format character) used. See the
244              sections below for more information.))
245 )
246 
247 $(SECTION4 Width$(COMMA) Precision and Separator)
248 
249 The $(I width) parameter specifies the minimum width of the result.
250 
251 The meaning of $(I precision) depends on the format indicator. For
252 integers it denotes the minimum number of digits printed, for
253 real numbers it denotes the number of fractional digits and for
254 strings and compound types it denotes the maximum number of elements
255 that are included in the output.
256 
257 A $(I separator) is used for formatting numbers. If it is specified,
258 the output is divided into chunks of three digits, separated by a $(B
259 ','). The number of digits in a chunk can be given explicitly by
260 providing a number or a $(B '*') after the $(B ',').
261 
262 In all three cases the number of digits can be replaced by a $(B
263 '*'). In this scenario, the next argument is used as the number of
264 digits. If the argument is a negative number, the $(I precision) and
265 $(I separator) parameters are considered unspecified. For $(I width),
266 the absolute value is used and the $(B '-') flag is set.
267 
268 The $(I separator) can also be followed by a $(B '?'). In that case,
269 an additional argument is used to specify the symbol that should be
270 used to separate the chunks.
271 
272 $(SECTION4 Position)
273 
274 By default, the arguments are processed in the provided order. With
275 the $(I position) parameter it is possible to address arguments
276 directly. It is also possible to denote a series of arguments with
277 two numbers separated by $(B ':'), that are all processed in the same
278 way. The second number can be omitted. In that case the series ends
279 with the last argument.
280 
281 It's also possible to use positional arguments for $(I width), $(I
282 precision) and $(I separator) by adding a number and a $(B
283 '$(DOLLAR)') after the $(B '*').
284 
285 $(SECTION4 Types)
286 
287 This section describes the result of combining types with format
288 characters. It is organized in 2 subsections: a list of general
289 information regarding the formatting of types in the presence of
290 format characters and a table that contains details for every
291 available combination of type and format character.
292 
293 When formatting types, the following rules apply:
294 
295 $(UL
296   $(LI If the format character is upper case, the resulting string will
297        be formatted using upper case letters.)
298   $(LI The default precision for floating point numbers is 6 digits.)
299   $(LI Rounding of floating point numbers adheres to the rounding mode
300        of the floating point unit, if available.)
301   $(LI The floating point values `NaN` and `Infinity` are formatted as
302        `nan` and `inf`, possibly preceded by $(B '+') or $(B '-') sign.)
303   $(LI Formatting reals is only supported for 64 bit reals and 80 bit reals.
304        All other reals are cast to double before they are formatted. This will
305        cause the result to be `inf` for very large numbers.)
306   $(LI Characters and strings formatted with the $(B 's') format character
307        inside of compound types are surrounded by single and double quotes
308        and unprintable characters are escaped. To avoid this, a $(B '-')
309        flag can be specified for the compound specifier
310        $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"` instead of `"%$(LPAREN)%s%$(RPAREN)"` $(RPAREN).)
311   $(LI Structs, unions, classes and interfaces are formatted by calling a
312        `toString` method if available.
313        See $(MREF_ALTTEXT $(D module std.format.write), std, format, write) for more
314        details.)
315   $(LI Only part of these combinations can be used for reading. See
316        $(MREF_ALTTEXT $(D module std.format.read), std, format, read) for more
317        detailed information.)
318 )
319 
320 This table contains descriptions for every possible combination of
321 type and format character:
322 
323 $(BOOKTABLE ,
324    $(TR $(THMINWIDTH Type) $(THMINWIDTH Format Character) $(TH Formatted as...))
325    $(TR $(MULTIROW_CELL 1, `null`)
326         $(TD $(B 's'))
327             $(TD `null`)
328    )
329    $(TR $(MULTIROW_CELL 3, `bool`)
330         $(TD $(B 's'))
331             $(TD `false` or `true`)
332    )
333    $(TR $(TD $(B 'b'), $(B 'd'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X'))
334             $(TD As the integrals 0 or 1 with the same format character.
335 
336             $(I Please note, that $(B 'o') and $(B 'x') with $(B '#') flag
337             might produce unexpected results due to special handling of
338             the value 0.))
339    )
340    $(TR $(TD $(B 'r'))
341             $(TD `\0` or `\1`)
342    )
343    $(TR $(MULTIROW_CELL 4, $(I Integral))
344         $(TD $(B 's'), $(B 'd'))
345             $(TD A signed decimal number. The $(B '#') flag is ignored.)
346    )
347    $(TR $(TD $(B 'b'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X'))
348             $(TD An unsigned binary, decimal, octal or hexadecimal number.
349 
350                  In case of $(B 'o') and $(B 'x'), the $(B '#') flag
351                  denotes that the number must be preceded by `0` and `0x`, with
352                  the exception of the value 0, where this does not apply. For
353                  $(B 'b') and $(B 'u') the $(B '#') flag has no effect.)
354    )
355    $(TR $(TD $(B 'e'), $(B 'E'), $(B 'f'), $(B 'F'), $(B 'g'), $(B 'G'), $(B 'a'), $(B 'A'))
356             $(TD As a floating point value with the same specifier.
357 
358                  Default precision is large enough to add all digits
359                  of the integral value.
360 
361                  In case of ($B 'a') and $(B 'A'), the integral digit can be
362                  any hexadecimal digit.
363                )
364    )
365    $(TR $(TD $(B 'r'))
366             $(TD Characters taken directly from the binary representation.)
367    )
368    $(TR $(MULTIROW_CELL 5, $(I Floating Point))
369         $(TD $(B 'e'), $(B 'E'))
370             $(TD Scientific notation: Exactly one integral digit followed by a dot
371                  and fractional digits, followed by the exponent.
372                  The exponent is formatted as $(B 'e') followed by
373                  a $(B '+') or $(B '-') sign, followed by at least
374                  two digits.
375 
376                  When there are no fractional digits and the $(B '#') flag
377                  is $(I not) present, the dot is omitted.)
378    )
379    $(TR $(TD $(B 'f'), $(B 'F'))
380             $(TD Natural notation: Integral digits followed by a dot and
381                  fractional digits.
382 
383                  When there are no fractional digits and the $(B '#') flag
384                  is $(I not) present, the dot is omitted.
385 
386                  $(I Please note: the difference between $(B 'f') and $(B 'F')
387                  is only visible for `NaN` and `Infinity`.))
388    )
389    $(TR $(TD $(B 's'), $(B 'g'), $(B 'G'))
390             $(TD Short notation: If the absolute value is larger than `10 ^^ precision`
391                  or smaller than `0.0001`, the scientific notation is used.
392                  If not, the natural notation is applied.
393 
394                  In both cases $(I precision) denotes the count of all digits, including
395                  the integral digits. Trailing zeros (including a trailing dot) are removed.
396 
397                  If $(B '#') flag is present, trailing zeros are not removed.)
398    )
399    $(TR $(TD $(B 'a'), $(B 'A'))
400             $(TD Hexadecimal scientific notation: `0x` followed by `1`
401                  (or `0` in case of value zero or denormalized number)
402                  followed by a dot, fractional digits in hexadecimal
403                  notation and an exponent. The exponent is build by `p`,
404                  followed by a sign and the exponent in $(I decimal) notation.
405 
406                  When there are no fractional digits and the $(B '#') flag
407                  is $(I not) present, the dot is omitted.)
408    )
409    $(TR $(TD $(B 'r'))
410             $(TD Characters taken directly from the binary representation.)
411    )
412    $(TR $(MULTIROW_CELL 3, $(I Character))
413         $(TD $(B 's'), $(B 'c'))
414             $(TD As the character.
415 
416                  Inside of a compound indicator $(B 's') is treated differently: The
417                  character is surrounded by single quotes and non printable
418                  characters are escaped. This can be avoided by preceding
419                  the compound indicator with a $(B '-') flag
420                  $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"`$(RPAREN).)
421    )
422    $(TR $(TD $(B 'b'), $(B 'd'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X'))
423             $(TD As the integral that represents the character.)
424    )
425    $(TR $(TD $(B 'r'))
426             $(TD Characters taken directly from the binary representation.)
427    )
428    $(TR $(MULTIROW_CELL 3, $(I String))
429         $(TD $(B 's'))
430             $(TD The sequence of characters that form the string.
431 
432                  Inside of a compound indicator the string is surrounded by double quotes
433                  and non printable characters are escaped. This can be avoided
434                  by preceding the compound indicator with a $(B '-') flag
435                  $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"`$(RPAREN).)
436    )
437    $(TR $(TD $(B 'r'))
438             $(TD The sequence of characters, each formatted with $(B 'r').)
439    )
440    $(TR $(TD compound)
441             $(TD As an array of characters.)
442    )
443    $(TR $(MULTIROW_CELL 3, $(I Array))
444         $(TD $(B 's'))
445             $(TD When the elements are characters, the array is formatted as
446                  a string. In all other cases the array is surrounded by square brackets
447                  and the elements are separated by a comma and a space. If the elements
448                  are strings, they are surrounded by double quotes and non
449                  printable characters are escaped.)
450    )
451    $(TR $(TD $(B 'r'))
452             $(TD The sequence of the elements, each formatted with $(B 'r').)
453    )
454    $(TR $(TD compound)
455             $(TD The sequence of the elements, each formatted according to the specifications
456                  given inside of the compound specifier.)
457    )
458    $(TR $(MULTIROW_CELL 2, $(I Associative Array))
459         $(TD $(B 's'))
460             $(TD As a sequence of the elements in unpredictable order. The output is
461                  surrounded by square brackets. The elements are separated by a
462                  comma and a space. The elements are formatted as `key:value`.)
463    )
464    $(TR $(TD compound)
465             $(TD As a sequence of the elements in unpredictable order. Each element
466                  is formatted according to the specifications given inside of the
467                  compound specifier. The first specifier is used for formatting
468                  the key and the second specifier is used for formatting the value.
469                  The order can be changed with positional arguments. For example
470                  `"%(%2$s (%1$s), %)"` will write the value, followed by the key in
471                  parenthesis.)
472    )
473    $(TR $(MULTIROW_CELL 2, $(I Enum))
474         $(TD $(B 's'))
475             $(TD The name of the value. If the name is not available, the base value
476                  is used, preceeded by a cast.)
477    )
478    $(TR $(TD All, but $(B 's'))
479             $(TD Enums can be formatted with all format characters that can be used
480                  with the base value. In that case they are formatted like the base value.)
481    )
482    $(TR $(MULTIROW_CELL 3, $(I Input Range))
483         $(TD $(B 's'))
484             $(TD When the elements of the range are characters, they are written like a string.
485                  In all other cases, the elements are enclosed by square brackets and separated
486                  by a comma and a space.)
487    )
488    $(TR $(TD $(B 'r'))
489             $(TD The sequence of the elements, each formatted with $(B 'r').)
490    )
491    $(TR $(TD compound)
492             $(TD The sequence of the elements, each formatted according to the specifications
493                  given inside of the compound specifier.)
494    )
495    $(TR $(MULTIROW_CELL 1, $(I Struct))
496         $(TD $(B 's'))
497             $(TD When the struct has neither an applicable `toString`
498                  nor is an input range, it is formatted as follows:
499                  `StructType(field1, field2, ...)`.)
500    )
501    $(TR $(MULTIROW_CELL 1, $(I Class))
502         $(TD $(B 's'))
503             $(TD When the class has neither an applicable `toString`
504                  nor is an input range, it is formatted as the
505                  fully qualified name of the class.)
506    )
507    $(TR $(MULTIROW_CELL 1, $(I Union))
508         $(TD $(B 's'))
509             $(TD When the union has neither an applicable `toString`
510                  nor is an input range, it is formatted as its base name.)
511    )
512    $(TR $(MULTIROW_CELL 2, $(I Pointer))
513         $(TD $(B 's'))
514             $(TD A null pointer is formatted as 'null'. All other pointers are
515                  formatted as hexadecimal numbers with the format character $(B 'X').)
516    )
517    $(TR $(TD $(B 'x'), $(B 'X'))
518             $(TD Formatted as a hexadecimal number.)
519    )
520    $(TR $(MULTIROW_CELL 3, $(I SIMD vector))
521         $(TD $(B 's'))
522             $(TD The array is surrounded by square brackets
523                  and the elements are separated by a comma and a space.)
524    )
525    $(TR $(TD $(B 'r'))
526             $(TD The sequence of the elements, each formatted with $(B 'r').)
527    )
528    $(TR $(TD compound)
529             $(TD The sequence of the elements, each formatted according to the specifications
530                  given inside of the compound specifier.)
531    )
532    $(TR $(MULTIROW_CELL 1, $(I Delegate))
533         $(TD $(B 's'), $(B 'r'), compound)
534             $(TD As the `.stringof` of this delegate treated as a string.
535 
536                  $(I Please note: The implementation is currently buggy
537                  and its use is discouraged.))
538    )
539 )
540 
541 Copyright: Copyright The D Language Foundation 2000-2021.
542 
543 Macros:
544 SUBREF = $(REF_ALTTEXT $2, $2, std, format, $1)$(NBSP)
545 MULTIROW_CELL = <td rowspan="$1">$+</td>
546 THMINWIDTH = <th scope="col" width="20%">$0</th>
547 
548 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
549 
550 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
551 Andrei Alexandrescu), and Kenji Hara
552 
553 Source: $(PHOBOSSRC std/format/package.d)
554  */
555 module std.format;
556 
557 /// Simple use:
558 @safe unittest
559 {
560     // Easiest way is to use `%s` everywhere:
561     assert(format("I got %s %s for %s euros.", 30, "eggs", 5.27) == "I got 30 eggs for 5.27 euros.");
562 
563     // Other format characters provide more control:
564     assert(format("I got %b %(%X%) for %f euros.", 30, "eggs", 5.27) == "I got 11110 65676773 for 5.270000 euros.");
565 }
566 
567 /// Compound specifiers allow formatting arrays and other compound types:
568 @safe unittest
569 {
570 /*
571 The trailing end of the sub-format string following the specifier for
572 each item is interpreted as the array delimiter, and is therefore
573 omitted following the last array item:
574  */
575     assert(format("My items are %(%s %).", [1,2,3]) == "My items are 1 2 3.");
576     assert(format("My items are %(%s, %).", [1,2,3]) == "My items are 1, 2, 3.");
577 
578 /*
579 The "%|" delimiter specifier may be used to indicate where the
580 delimiter begins, so that the portion of the format string prior to
581 it will be retained in the last array element:
582  */
583     assert(format("My items are %(-%s-%|, %).", [1,2,3]) == "My items are -1-, -2-, -3-.");
584 
585 /*
586 These compound format specifiers may be nested in the case of a
587 nested array argument:
588  */
589     auto mat = [[1, 2, 3],
590                 [4, 5, 6],
591                 [7, 8, 9]];
592 
593     assert(format("%(%(%d %) - %)", mat), "1 2 3 - 4 5 6 - 7 8 9");
594     assert(format("[%(%(%d %) - %)]", mat), "[1 2 3 - 4 5 6 - 7 8 9]");
595     assert(format("[%([%(%d %)]%| - %)]", mat), "[1 2 3] - [4 5 6] - [7 8 9]");
596 
597 /*
598 Strings and characters are escaped automatically inside compound
599 format specifiers. To avoid this behavior, use "%-(" instead of "%(":
600  */
601     assert(format("My friends are %s.", ["John", "Nancy"]) == `My friends are ["John", "Nancy"].`);
602     assert(format("My friends are %(%s, %).", ["John", "Nancy"]) == `My friends are "John", "Nancy".`);
603     assert(format("My friends are %-(%s, %).", ["John", "Nancy"]) == `My friends are John, Nancy.`);
604 }
605 
606 /// Using parameters:
607 @safe unittest
608 {
609     // Flags can be used to influence to outcome:
610     assert(format("%g != %+#g", 3.14, 3.14) == "3.14 != +3.14000");
611 
612     // Width and precision help to arrange the formatted result:
613     assert(format(">%10.2f<", 1234.56789) == ">   1234.57<");
614 
615     // Numbers can be grouped:
616     assert(format("%,4d", int.max) == "21,4748,3647");
617 
618     // It's possible to specify the position of an argument:
619     assert(format("%3$s %1$s", 3, 17, 5) == "5 3");
620 }
621 
622 /// Providing parameters as arguments:
623 @safe unittest
624 {
625     // Width as argument
626     assert(format(">%*s<", 10, "abc") == ">       abc<");
627 
628     // Precision as argument
629     assert(format(">%.*f<", 5, 123.2) == ">123.20000<");
630 
631     // Grouping as argument
632     assert(format("%,*d", 1, int.max) == "2,1,4,7,4,8,3,6,4,7");
633 
634     // Grouping separator as argument
635     assert(format("%,3?d", '_', int.max) == "2_147_483_647");
636 
637     // All at once
638     assert(format("%*.*,*?d", 20, 15, 6, '/', int.max) == "   000/002147/483647");
639 }
640 
641 public import std.format.read;
642 public import std.format.spec;
643 public import std.format.write;
644 
645 import std.exception : enforce;
646 import std.range.primitives : isInputRange;
647 import std.traits : CharTypeOf, isSomeChar, isSomeString, StringTypeOf;
648 import std.format.internal.write : hasToString;
649 
650 /**
651 Signals an issue encountered while formatting.
652  */
653 class FormatException : Exception
654 {
655     /// Generic constructor.
656     @safe @nogc pure nothrow
657     this()
658     {
659         super("format error");
660     }
661 
662     /**
663        Creates a new instance of `FormatException`.
664 
665        Params:
666            msg = message of the exception
667            fn = file name of the file where the exception was created (optional)
668            ln = line number of the file where the exception was created (optional)
669            next = for internal use, should always be null (optional)
670      */
671     @safe @nogc pure nothrow
672     this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
673     {
674         super(msg, fn, ln, next);
675     }
676 }
677 
678 ///
679 @safe unittest
680 {
681     import std.exception : assertThrown;
682 
683     assertThrown!FormatException(format("%d", "foo"));
684 }
685 
686 package alias enforceFmt = enforce!FormatException;
687 
688 // @@@DEPRECATED_[2.107.0]@@@
689 deprecated("formatElement was accidentally made public and will be removed in 2.107.0")
690 void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
691 if (is(StringTypeOf!T) && !hasToString!(T, Char) && !is(T == enum))
692 {
693     import std.format.internal.write : fe = formatElement;
694 
695     fe(w, val, f);
696 }
697 
698 // @@@DEPRECATED_[2.107.0]@@@
699 deprecated("formatElement was accidentally made public and will be removed in 2.107.0")
700 void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
701 if (is(CharTypeOf!T) && !is(T == enum))
702 {
703     import std.format.internal.write : fe = formatElement;
704 
705     fe(w, val, f);
706 }
707 
708 // @@@DEPRECATED_[2.107.0]@@@
709 deprecated("formatElement was accidentally made public and will be removed in 2.107.0")
710 void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f)
711 if ((!is(StringTypeOf!T) || hasToString!(T, Char)) && !is(CharTypeOf!T) || is(T == enum))
712 {
713     import std.format.internal.write : fe = formatElement;
714 
715     fe(w, val, f);
716 }
717 
718 // Like NullSink, but toString() isn't even called at all. Used to test the format string.
719 package struct NoOpSink
720 {
721     void put(E)(scope const E) pure @safe @nogc nothrow {}
722 }
723 
724 // @@@DEPRECATED_[2.107.0]@@@
725 deprecated("unformatElement was accidentally made public and will be removed in 2.107.0")
726 T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
727 if (isInputRange!Range)
728 {
729     import std.format.internal.read : ue = unformatElement;
730 
731     return ue(input, spec);
732 }
733 
734 // Used to check format strings are compatible with argument types
735 package(std) enum checkFormatException(alias fmt, Args...) =
736 {
737     import std.conv : text;
738 
739     try
740     {
741         auto n = .formattedWrite(NoOpSink(), fmt, Args.init);
742 
743         enforceFmt(n == Args.length, text("Orphan format arguments: args[", n, "..", Args.length, "]"));
744     }
745     catch (Exception e)
746         return e.msg;
747     return null;
748 }();
749 
750 /**
751 Converts its arguments according to a format string into a string.
752 
753 The second version of `format` takes the format string as template
754 argument. In this case, it is checked for consistency at
755 compile-time and produces slightly faster code, because the length of
756 the output buffer can be estimated in advance.
757 
758 Params:
759     fmt = a $(MREF_ALTTEXT format string, std,format)
760     args = a variadic list of arguments to be formatted
761     Char = character type of `fmt`
762     Args = a variadic list of types of the arguments
763 
764 Returns:
765     The formatted string.
766 
767 Throws:
768     A $(LREF FormatException) if formatting did not succeed.
769 
770 See_Also:
771     $(LREF sformat) for a variant, that tries to avoid garbage collection.
772  */
773 immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
774 if (isSomeChar!Char)
775 {
776     import std.array : appender;
777 
778     auto w = appender!(immutable(Char)[]);
779     auto n = formattedWrite(w, fmt, args);
780     version (all)
781     {
782         // In the future, this check will be removed to increase consistency
783         // with formattedWrite
784         import std.conv : text;
785         enforceFmt(n == args.length, text("Orphan format arguments: args[", n, "..", args.length, "]"));
786     }
787     return w.data;
788 }
789 
790 ///
791 @safe pure unittest
792 {
793     assert(format("Here are %d %s.", 3, "apples") == "Here are 3 apples.");
794 
795     assert("Increase: %7.2f %%".format(17.4285) == "Increase:   17.43 %");
796 }
797 
798 @safe pure unittest
799 {
800     import std.exception : assertCTFEable, assertThrown;
801 
802     assertCTFEable!(
803     {
804         assert(format("foo") == "foo");
805         assert(format("foo%%") == "foo%");
806         assert(format("foo%s", 'C') == "fooC");
807         assert(format("%s foo", "bar") == "bar foo");
808         assert(format("%s foo %s", "bar", "abc") == "bar foo abc");
809         assert(format("foo %d", -123) == "foo -123");
810         assert(format("foo %d", 123) == "foo 123");
811 
812         assertThrown!FormatException(format("foo %s"));
813         assertThrown!FormatException(format("foo %s", 123, 456));
814 
815         assert(format("hel%slo%s%s%s", "world", -138, 'c', true) == "helworldlo-138ctrue");
816     });
817 
818     assert(is(typeof(format("happy")) == string));
819     assert(is(typeof(format("happy"w)) == wstring));
820     assert(is(typeof(format("happy"d)) == dstring));
821 }
822 
823 // https://issues.dlang.org/show_bug.cgi?id=16661
824 @safe pure unittest
825 {
826     assert(format("%.2f"d, 0.4) == "0.40");
827     assert("%02d"d.format(1) == "01"d);
828 }
829 
830 @safe unittest
831 {
832     int i;
833     string s;
834 
835     s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo");
836     assert(s == "hello world! true 57 1000000000x foo");
837 
838     s = format("%s %A %s", 1.67, -1.28, float.nan);
839     assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s);
840 
841     s = format("%x %X", 0x1234AF, 0xAFAFAFAF);
842     assert(s == "1234af AFAFAFAF");
843 
844     s = format("%b %o", 0x1234AF, 0xAFAFAFAF);
845     assert(s == "100100011010010101111 25753727657");
846 
847     s = format("%d %s", 0x1234AF, 0xAFAFAFAF);
848     assert(s == "1193135 2947526575");
849 }
850 
851 @safe unittest
852 {
853     import std.conv : octal;
854 
855     string s;
856     int i;
857 
858     s = format("%#06.*f", 2, 12.345);
859     assert(s == "012.35");
860 
861     s = format("%#0*.*f", 6, 2, 12.345);
862     assert(s == "012.35");
863 
864     s = format("%7.4g:", 12.678);
865     assert(s == "  12.68:");
866 
867     s = format("%7.4g:", 12.678L);
868     assert(s == "  12.68:");
869 
870     s = format("%04f|%05d|%#05x|%#5x", -4.0, -10, 1, 1);
871     assert(s == "-4.000000|-0010|0x001|  0x1");
872 
873     i = -10;
874     s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
875     assert(s == "-10|-10|-10|-10|-10.0000");
876 
877     i = -5;
878     s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
879     assert(s == "-5| -5|-05|-5|-5.0000");
880 
881     i = 0;
882     s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
883     assert(s == "0|  0|000|0|0.0000");
884 
885     i = 5;
886     s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
887     assert(s == "5|  5|005|5|5.0000");
888 
889     i = 10;
890     s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
891     assert(s == "10| 10|010|10|10.0000");
892 
893     s = format("%.0d", 0);
894     assert(s == "0");
895 
896     s = format("%.g", .34);
897     assert(s == "0.3");
898 
899     s = format("%.0g", .34);
900     assert(s == "0.3");
901 
902     s = format("%.2g", .34);
903     assert(s == "0.34");
904 
905     s = format("%0.0008f", 1e-08);
906     assert(s == "0.00000001");
907 
908     s = format("%0.0008f", 1e-05);
909     assert(s == "0.00001000");
910 
911     s = "helloworld";
912     string r;
913     r = format("%.2s", s[0 .. 5]);
914     assert(r == "he");
915     r = format("%.20s", s[0 .. 5]);
916     assert(r == "hello");
917     r = format("%8s", s[0 .. 5]);
918     assert(r == "   hello");
919 
920     byte[] arrbyte = new byte[4];
921     arrbyte[0] = 100;
922     arrbyte[1] = -99;
923     arrbyte[3] = 0;
924     r = format("%s", arrbyte);
925     assert(r == "[100, -99, 0, 0]");
926 
927     ubyte[] arrubyte = new ubyte[4];
928     arrubyte[0] = 100;
929     arrubyte[1] = 200;
930     arrubyte[3] = 0;
931     r = format("%s", arrubyte);
932     assert(r == "[100, 200, 0, 0]");
933 
934     short[] arrshort = new short[4];
935     arrshort[0] = 100;
936     arrshort[1] = -999;
937     arrshort[3] = 0;
938     r = format("%s", arrshort);
939     assert(r == "[100, -999, 0, 0]");
940 
941     ushort[] arrushort = new ushort[4];
942     arrushort[0] = 100;
943     arrushort[1] = 20_000;
944     arrushort[3] = 0;
945     r = format("%s", arrushort);
946     assert(r == "[100, 20000, 0, 0]");
947 
948     int[] arrint = new int[4];
949     arrint[0] = 100;
950     arrint[1] = -999;
951     arrint[3] = 0;
952     r = format("%s", arrint);
953     assert(r == "[100, -999, 0, 0]");
954 
955     long[] arrlong = new long[4];
956     arrlong[0] = 100;
957     arrlong[1] = -999;
958     arrlong[3] = 0;
959     r = format("%s", arrlong);
960     assert(r == "[100, -999, 0, 0]");
961 
962     ulong[] arrulong = new ulong[4];
963     arrulong[0] = 100;
964     arrulong[1] = 999;
965     arrulong[3] = 0;
966     r = format("%s", arrulong);
967     assert(r == "[100, 999, 0, 0]");
968 
969     string[] arr2 = new string[4];
970     arr2[0] = "hello";
971     arr2[1] = "world";
972     arr2[3] = "foo";
973     r = format("%s", arr2);
974     assert(r == `["hello", "world", "", "foo"]`);
975 
976     r = format("%.8d", 7);
977     assert(r == "00000007");
978     r = format("%.8x", 10);
979     assert(r == "0000000a");
980 
981     r = format("%-3d", 7);
982     assert(r == "7  ");
983 
984     r = format("%-1*d", 4, 3);
985     assert(r == "3   ");
986 
987     r = format("%*d", -3, 7);
988     assert(r == "7  ");
989 
990     r = format("%.*d", -3, 7);
991     assert(r == "7");
992 
993     r = format("%-1.*f", 2, 3.1415);
994     assert(r == "3.14");
995 
996     r = format("abc"c);
997     assert(r == "abc");
998 
999     //format() returns the same type as inputted.
1000     wstring wr;
1001     wr = format("def"w);
1002     assert(wr == "def"w);
1003 
1004     dstring dr;
1005     dr = format("ghi"d);
1006     assert(dr == "ghi"d);
1007 
1008     // Empty static character arrays work as well
1009     const char[0] cempty;
1010     assert(format("test%spath", cempty) == "testpath");
1011     const wchar[0] wempty;
1012     assert(format("test%spath", wempty) == "testpath");
1013     const dchar[0] dempty;
1014     assert(format("test%spath", dempty) == "testpath");
1015 
1016     void* p = () @trusted { return cast(void*) 0xDEADBEEF; } ();
1017     r = format("%s", p);
1018     assert(r == "DEADBEEF");
1019 
1020     r = format("%#x", 0xabcd);
1021     assert(r == "0xabcd");
1022     r = format("%#X", 0xABCD);
1023     assert(r == "0XABCD");
1024 
1025     r = format("%#o", octal!12345);
1026     assert(r == "012345");
1027     r = format("%o", 9);
1028     assert(r == "11");
1029     r = format("%#o", 0);   // https://issues.dlang.org/show_bug.cgi?id=15663
1030     assert(r == "0");
1031 
1032     r = format("%+d", 123);
1033     assert(r == "+123");
1034     r = format("%+d", -123);
1035     assert(r == "-123");
1036     r = format("% d", 123);
1037     assert(r == " 123");
1038     r = format("% d", -123);
1039     assert(r == "-123");
1040 
1041     r = format("%%");
1042     assert(r == "%");
1043 
1044     r = format("%d", true);
1045     assert(r == "1");
1046     r = format("%d", false);
1047     assert(r == "0");
1048 
1049     r = format("%d", 'a');
1050     assert(r == "97");
1051     wchar wc = 'a';
1052     r = format("%d", wc);
1053     assert(r == "97");
1054     dchar dc = 'a';
1055     r = format("%d", dc);
1056     assert(r == "97");
1057 
1058     byte b = byte.max;
1059     r = format("%x", b);
1060     assert(r == "7f");
1061     r = format("%x", ++b);
1062     assert(r == "80");
1063     r = format("%x", ++b);
1064     assert(r == "81");
1065 
1066     short sh = short.max;
1067     r = format("%x", sh);
1068     assert(r == "7fff");
1069     r = format("%x", ++sh);
1070     assert(r == "8000");
1071     r = format("%x", ++sh);
1072     assert(r == "8001");
1073 
1074     i = int.max;
1075     r = format("%x", i);
1076     assert(r == "7fffffff");
1077     r = format("%x", ++i);
1078     assert(r == "80000000");
1079     r = format("%x", ++i);
1080     assert(r == "80000001");
1081 
1082     r = format("%x", 10);
1083     assert(r == "a");
1084     r = format("%X", 10);
1085     assert(r == "A");
1086     r = format("%x", 15);
1087     assert(r == "f");
1088     r = format("%X", 15);
1089     assert(r == "F");
1090 
1091     Object c = null;
1092     r = () @trusted { return format("%s", c); } ();
1093     assert(r == "null");
1094 
1095     enum TestEnum
1096     {
1097         Value1, Value2
1098     }
1099     r = format("%s", TestEnum.Value2);
1100     assert(r == "Value2");
1101 
1102     immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
1103     r = () @trusted { return format("%s", aa.values); } ();
1104     assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`);
1105     r = format("%s", aa);
1106     assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`);
1107 
1108     static const dchar[] ds = ['a','b'];
1109     for (int j = 0; j < ds.length; ++j)
1110     {
1111         r = format(" %d", ds[j]);
1112         if (j == 0)
1113             assert(r == " 97");
1114         else
1115             assert(r == " 98");
1116     }
1117 
1118     r = format(">%14d<, %s", 15, [1,2,3]);
1119     assert(r == ">            15<, [1, 2, 3]");
1120 
1121     assert(format("%8s", "bar") == "     bar");
1122     assert(format("%8s", "b\u00e9ll\u00f4") == "   b\u00e9ll\u00f4");
1123 }
1124 
1125 @safe unittest
1126 {
1127     import std.exception : assertCTFEable;
1128 
1129     assertCTFEable!(
1130     {
1131         auto tmp = format("%,d", 1000);
1132         assert(tmp == "1,000", "'" ~ tmp ~ "'");
1133 
1134         tmp = format("%,?d", 'z', 1234567);
1135         assert(tmp == "1z234z567", "'" ~ tmp ~ "'");
1136 
1137         tmp = format("%10,?d", 'z', 1234567);
1138         assert(tmp == " 1z234z567", "'" ~ tmp ~ "'");
1139 
1140         tmp = format("%11,2?d", 'z', 1234567);
1141         assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
1142 
1143         tmp = format("%11,*?d", 2, 'z', 1234567);
1144         assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
1145 
1146         tmp = format("%11,*d", 2, 1234567);
1147         assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
1148 
1149         tmp = format("%11,2d", 1234567);
1150         assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
1151     });
1152 }
1153 
1154 @safe unittest
1155 {
1156     auto tmp = format("%,f", 1000.0);
1157     assert(tmp == "1,000.000000", "'" ~ tmp ~ "'");
1158 
1159     tmp = format("%,f", 1234567.891011);
1160     assert(tmp == "1,234,567.891011", "'" ~ tmp ~ "'");
1161 
1162     tmp = format("%,f", -1234567.891011);
1163     assert(tmp == "-1,234,567.891011", "'" ~ tmp ~ "'");
1164 
1165     tmp = format("%,2f", 1234567.891011);
1166     assert(tmp == "1,23,45,67.891011", "'" ~ tmp ~ "'");
1167 
1168     tmp = format("%18,f", 1234567.891011);
1169     assert(tmp == "  1,234,567.891011", "'" ~ tmp ~ "'");
1170 
1171     tmp = format("%18,?f", '.', 1234567.891011);
1172     assert(tmp == "  1.234.567.891011", "'" ~ tmp ~ "'");
1173 
1174     tmp = format("%,?.3f", 'ä', 1234567.891011);
1175     assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'");
1176 
1177     tmp = format("%,*?.3f", 1, 'ä', 1234567.891011);
1178     assert(tmp == "1ä2ä3ä4ä5ä6ä7.891", "'" ~ tmp ~ "'");
1179 
1180     tmp = format("%,4?.3f", '_', 1234567.891011);
1181     assert(tmp == "123_4567.891", "'" ~ tmp ~ "'");
1182 
1183     tmp = format("%12,3.3f", 1234.5678);
1184     assert(tmp == "   1,234.568", "'" ~ tmp ~ "'");
1185 
1186     tmp = format("%,e", 3.141592653589793238462);
1187     assert(tmp == "3.141593e+00", "'" ~ tmp ~ "'");
1188 
1189     tmp = format("%15,e", 3.141592653589793238462);
1190     assert(tmp == "   3.141593e+00", "'" ~ tmp ~ "'");
1191 
1192     tmp = format("%15,e", -3.141592653589793238462);
1193     assert(tmp == "  -3.141593e+00", "'" ~ tmp ~ "'");
1194 
1195     tmp = format("%.4,*e", 2, 3.141592653589793238462);
1196     assert(tmp == "3.1416e+00", "'" ~ tmp ~ "'");
1197 
1198     tmp = format("%13.4,*e", 2, 3.141592653589793238462);
1199     assert(tmp == "   3.1416e+00", "'" ~ tmp ~ "'");
1200 
1201     tmp = format("%,.0f", 3.14);
1202     assert(tmp == "3", "'" ~ tmp ~ "'");
1203 
1204     tmp = format("%3,g", 1_000_000.123456);
1205     assert(tmp == "1e+06", "'" ~ tmp ~ "'");
1206 
1207     tmp = format("%19,?f", '.', -1234567.891011);
1208     assert(tmp == "  -1.234.567.891011", "'" ~ tmp ~ "'");
1209 }
1210 
1211 // Test for multiple indexes
1212 @safe unittest
1213 {
1214     auto tmp = format("%2:5$s", 1, 2, 3, 4, 5);
1215     assert(tmp == "2345", tmp);
1216 }
1217 
1218 // https://issues.dlang.org/show_bug.cgi?id=18047
1219 @safe unittest
1220 {
1221     auto cmp = "     123,456";
1222     assert(cmp.length == 12, format("%d", cmp.length));
1223     auto tmp = format("%12,d", 123456);
1224     assert(tmp.length == 12, format("%d", tmp.length));
1225 
1226     assert(tmp == cmp, "'" ~ tmp ~ "'");
1227 }
1228 
1229 // https://issues.dlang.org/show_bug.cgi?id=17459
1230 @safe unittest
1231 {
1232     auto cmp = "100";
1233     auto tmp  = format("%0d", 100);
1234     assert(tmp == cmp, tmp);
1235 
1236     cmp = "0100";
1237     tmp  = format("%04d", 100);
1238     assert(tmp == cmp, tmp);
1239 
1240     cmp = "0,000,000,100";
1241     tmp  = format("%012,3d", 100);
1242     assert(tmp == cmp, tmp);
1243 
1244     cmp = "0,000,001,000";
1245     tmp = format("%012,3d", 1_000);
1246     assert(tmp == cmp, tmp);
1247 
1248     cmp = "0,000,100,000";
1249     tmp = format("%012,3d", 100_000);
1250     assert(tmp == cmp, tmp);
1251 
1252     cmp = "0,001,000,000";
1253     tmp = format("%012,3d", 1_000_000);
1254     assert(tmp == cmp, tmp);
1255 
1256     cmp = "0,100,000,000";
1257     tmp = format("%012,3d", 100_000_000);
1258     assert(tmp == cmp, tmp);
1259 }
1260 
1261 // https://issues.dlang.org/show_bug.cgi?id=17459
1262 @safe unittest
1263 {
1264     auto cmp = "100,000";
1265     auto tmp  = format("%06,d", 100_000);
1266     assert(tmp == cmp, tmp);
1267 
1268     cmp = "100,000";
1269     tmp  = format("%07,d", 100_000);
1270     assert(tmp == cmp, tmp);
1271 
1272     cmp = "0,100,000";
1273     tmp  = format("%08,d", 100_000);
1274     assert(tmp == cmp, tmp);
1275 }
1276 
1277 // https://issues.dlang.org/show_bug.cgi?id=20288
1278 @safe unittest
1279 {
1280     string s = format("%,.2f", double.nan);
1281     assert(s == "nan", s);
1282 
1283     s = format("%,.2F", double.nan);
1284     assert(s == "NAN", s);
1285 
1286     s = format("%,.2f", -double.nan);
1287     assert(s == "-nan", s);
1288 
1289     s = format("%,.2F", -double.nan);
1290     assert(s == "-NAN", s);
1291 
1292     string g = format("^%13s$", "nan");
1293     string h = "^          nan$";
1294     assert(g == h, "\ngot:" ~ g ~ "\nexp:" ~ h);
1295     string a = format("^%13,3.2f$", double.nan);
1296     string b = format("^%13,3.2F$", double.nan);
1297     string c = format("^%13,3.2f$", -double.nan);
1298     string d = format("^%13,3.2F$", -double.nan);
1299     assert(a == "^          nan$", "\ngot:'"~ a ~ "'\nexp:'^          nan$'");
1300     assert(b == "^          NAN$", "\ngot:'"~ b ~ "'\nexp:'^          NAN$'");
1301     assert(c == "^         -nan$", "\ngot:'"~ c ~ "'\nexp:'^         -nan$'");
1302     assert(d == "^         -NAN$", "\ngot:'"~ d ~ "'\nexp:'^         -NAN$'");
1303 
1304     a = format("^%-13,3.2f$", double.nan);
1305     b = format("^%-13,3.2F$", double.nan);
1306     c = format("^%-13,3.2f$", -double.nan);
1307     d = format("^%-13,3.2F$", -double.nan);
1308     assert(a == "^nan          $", "\ngot:'"~ a ~ "'\nexp:'^nan          $'");
1309     assert(b == "^NAN          $", "\ngot:'"~ b ~ "'\nexp:'^NAN          $'");
1310     assert(c == "^-nan         $", "\ngot:'"~ c ~ "'\nexp:'^-nan         $'");
1311     assert(d == "^-NAN         $", "\ngot:'"~ d ~ "'\nexp:'^-NAN         $'");
1312 
1313     a = format("^%+13,3.2f$", double.nan);
1314     b = format("^%+13,3.2F$", double.nan);
1315     c = format("^%+13,3.2f$", -double.nan);
1316     d = format("^%+13,3.2F$", -double.nan);
1317     assert(a == "^         +nan$", "\ngot:'"~ a ~ "'\nexp:'^         +nan$'");
1318     assert(b == "^         +NAN$", "\ngot:'"~ b ~ "'\nexp:'^         +NAN$'");
1319     assert(c == "^         -nan$", "\ngot:'"~ c ~ "'\nexp:'^         -nan$'");
1320     assert(d == "^         -NAN$", "\ngot:'"~ d ~ "'\nexp:'^         -NAN$'");
1321 
1322     a = format("^%-+13,3.2f$", double.nan);
1323     b = format("^%-+13,3.2F$", double.nan);
1324     c = format("^%-+13,3.2f$", -double.nan);
1325     d = format("^%-+13,3.2F$", -double.nan);
1326     assert(a == "^+nan         $", "\ngot:'"~ a ~ "'\nexp:'^+nan         $'");
1327     assert(b == "^+NAN         $", "\ngot:'"~ b ~ "'\nexp:'^+NAN         $'");
1328     assert(c == "^-nan         $", "\ngot:'"~ c ~ "'\nexp:'^-nan         $'");
1329     assert(d == "^-NAN         $", "\ngot:'"~ d ~ "'\nexp:'^-NAN         $'");
1330 
1331     a = format("^%- 13,3.2f$", double.nan);
1332     b = format("^%- 13,3.2F$", double.nan);
1333     c = format("^%- 13,3.2f$", -double.nan);
1334     d = format("^%- 13,3.2F$", -double.nan);
1335     assert(a == "^ nan         $", "\ngot:'"~ a ~ "'\nexp:'^ nan         $'");
1336     assert(b == "^ NAN         $", "\ngot:'"~ b ~ "'\nexp:'^ NAN         $'");
1337     assert(c == "^-nan         $", "\ngot:'"~ c ~ "'\nexp:'^-nan         $'");
1338     assert(d == "^-NAN         $", "\ngot:'"~ d ~ "'\nexp:'^-NAN         $'");
1339 }
1340 
1341 @safe unittest
1342 {
1343     struct S
1344     {
1345         int a;
1346 
1347         void toString(void delegate(const(char)[]) sink, string fmt)
1348         {
1349             auto spec = singleSpec(fmt);
1350             sink.formatValue(a, spec);
1351         }
1352     }
1353 
1354     S s = S(1);
1355     auto result = () @trusted { return format!"%5,3d"(s); } ();
1356     assert(result == "    1");
1357 }
1358 
1359 // https://issues.dlang.org/show_bug.cgi?id=23245
1360 @safe unittest
1361 {
1362     static struct S
1363     {
1364         string toString() { return "S"; }
1365     }
1366 
1367     S[1] s;
1368     assert(format("%s", s) == "[S]");
1369 }
1370 
1371 // https://issues.dlang.org/show_bug.cgi?id=23246
1372 @safe unittest
1373 {
1374     static struct S
1375     {
1376         string toString() { return "S"; }
1377     }
1378 
1379     S[int] s = [0 : S()];
1380     assert(format("%s", s) == "[0:S]");
1381 }
1382 
1383 /// ditto
1384 typeof(fmt) format(alias fmt, Args...)(Args args)
1385 if (isSomeString!(typeof(fmt)))
1386 {
1387     import std.array : appender;
1388     import std.range.primitives : ElementEncodingType;
1389     import std.traits : Unqual;
1390 
1391     alias e = checkFormatException!(fmt, Args);
1392     alias Char = Unqual!(ElementEncodingType!(typeof(fmt)));
1393 
1394     static assert(!e, e);
1395     auto w = appender!(immutable(Char)[]);
1396 
1397     // no need to traverse the string twice during compile time
1398     if (!__ctfe)
1399     {
1400         enum len = guessLength!Char(fmt);
1401         w.reserve(len);
1402     }
1403     else
1404     {
1405         w.reserve(fmt.length);
1406     }
1407 
1408     formattedWrite(w, fmt, args);
1409     return w.data;
1410 }
1411 
1412 /// The format string can be checked at compile-time:
1413 @safe pure unittest
1414 {
1415     auto s = format!"%s is %s"("Pi", 3.14);
1416     assert(s == "Pi is 3.14");
1417 
1418     // This line doesn't compile, because 3.14 cannot be formatted with %d:
1419     // s = format!"%s is %d"("Pi", 3.14);
1420 }
1421 
1422 @safe pure unittest
1423 {
1424     string s;
1425     static assert(!__traits(compiles, {s = format!"%l"();}));     // missing arg
1426     static assert(!__traits(compiles, {s = format!""(404);}));    // surplus arg
1427     static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg
1428 }
1429 
1430 // https://issues.dlang.org/show_bug.cgi?id=17381
1431 @safe pure unittest
1432 {
1433     static assert(!__traits(compiles, format!"%s"(1.5, 2)));
1434     static assert(!__traits(compiles, format!"%f"(1.5, 2)));
1435     static assert(!__traits(compiles, format!"%s"(1.5L, 2)));
1436     static assert(!__traits(compiles, format!"%f"(1.5L, 2)));
1437 }
1438 
1439 // called during compilation to guess the length of the
1440 // result of format
1441 private size_t guessLength(Char, S)(S fmtString)
1442 {
1443     import std.array : appender;
1444 
1445     size_t len;
1446     auto output = appender!(immutable(Char)[])();
1447     auto spec = FormatSpec!Char(fmtString);
1448     while (spec.writeUpToNextSpec(output))
1449     {
1450         // take a guess
1451         if (spec.width == 0 && (spec.precision == spec.UNSPECIFIED || spec.precision == spec.DYNAMIC))
1452         {
1453             switch (spec.spec)
1454             {
1455                 case 'c':
1456                     ++len;
1457                     break;
1458                 case 'd':
1459                 case 'x':
1460                 case 'X':
1461                     len += 3;
1462                     break;
1463                 case 'b':
1464                     len += 8;
1465                     break;
1466                 case 'f':
1467                 case 'F':
1468                     len += 10;
1469                     break;
1470                 case 's':
1471                 case 'e':
1472                 case 'E':
1473                 case 'g':
1474                 case 'G':
1475                     len += 12;
1476                     break;
1477                 default: break;
1478             }
1479 
1480             continue;
1481         }
1482 
1483         if ((spec.spec == 'e' || spec.spec == 'E' || spec.spec == 'g' ||
1484              spec.spec == 'G' || spec.spec == 'f' || spec.spec == 'F') &&
1485             spec.precision != spec.UNSPECIFIED && spec.precision != spec.DYNAMIC &&
1486             spec.width == 0
1487         )
1488         {
1489             len += spec.precision + 5;
1490             continue;
1491         }
1492 
1493         if (spec.width == spec.precision)
1494             len += spec.width;
1495         else if (spec.width > 0 && spec.width != spec.DYNAMIC &&
1496                  (spec.precision == spec.UNSPECIFIED || spec.width > spec.precision))
1497         {
1498             len += spec.width;
1499         }
1500         else if (spec.precision != spec.UNSPECIFIED && spec.precision > spec.width)
1501             len += spec.precision;
1502     }
1503     len += output.data.length;
1504     return len;
1505 }
1506 
1507 @safe pure
1508 unittest
1509 {
1510     assert(guessLength!char("%c") == 1);
1511     assert(guessLength!char("%d") == 3);
1512     assert(guessLength!char("%x") == 3);
1513     assert(guessLength!char("%b") == 8);
1514     assert(guessLength!char("%f") == 10);
1515     assert(guessLength!char("%s") == 12);
1516     assert(guessLength!char("%02d") == 2);
1517     assert(guessLength!char("%02d") == 2);
1518     assert(guessLength!char("%4.4d") == 4);
1519     assert(guessLength!char("%2.4f") == 4);
1520     assert(guessLength!char("%02d:%02d:%02d") == 8);
1521     assert(guessLength!char("%0.2f") == 7);
1522     assert(guessLength!char("%0*d") == 0);
1523 }
1524 
1525 /**
1526 Converts its arguments according to a format string into a buffer.
1527 The buffer has to be large enough to hold the formatted string.
1528 
1529 The second version of `sformat` takes the format string as a template
1530 argument. In this case, it is checked for consistency at
1531 compile-time.
1532 
1533 Params:
1534     buf = the buffer where the formatted string should go
1535     fmt = a $(MREF_ALTTEXT format string, std,format)
1536     args = a variadic list of arguments to be formatted
1537     Char = character type of `fmt`
1538     Args = a variadic list of types of the arguments
1539 
1540 Returns:
1541     A slice of `buf` containing the formatted string.
1542 
1543 Throws:
1544     A $(REF_ALTTEXT RangeError, RangeError, core, exception) if `buf`
1545     isn't large enough to hold the formatted string
1546     and a $(LREF FormatException) if formatting did not succeed.
1547 
1548 Note:
1549     In theory this function should be `@nogc`. But with the current
1550     implementation there are some cases where allocations occur:
1551 
1552     $(UL
1553     $(LI An exception is thrown.)
1554     $(LI A custom `toString` function of a compound type allocates.))
1555  */
1556 char[] sformat(Char, Args...)(return scope char[] buf, scope const(Char)[] fmt, Args args)
1557 {
1558     import core.exception : RangeError;
1559     import std.range.primitives;
1560     import std.utf : encode;
1561 
1562     static struct Sink
1563     {
1564         char[] buf;
1565         size_t i;
1566         void put(char c)
1567         {
1568             if (buf.length <= i)
1569                 throw new RangeError(__FILE__, __LINE__);
1570 
1571             buf[i] = c;
1572             i += 1;
1573         }
1574         void put(dchar c)
1575         {
1576             char[4] enc;
1577             auto n = encode(enc, c);
1578 
1579             if (buf.length < i + n)
1580                 throw new RangeError(__FILE__, __LINE__);
1581 
1582             buf[i .. i + n] = enc[0 .. n];
1583             i += n;
1584         }
1585         void put(scope const(char)[] s)
1586         {
1587             if (buf.length < i + s.length)
1588                 throw new RangeError(__FILE__, __LINE__);
1589 
1590             buf[i .. i + s.length] = s[];
1591             i += s.length;
1592         }
1593         void put(scope const(wchar)[] s)
1594         {
1595             for (; !s.empty; s.popFront())
1596                 put(s.front);
1597         }
1598         void put(scope const(dchar)[] s)
1599         {
1600             for (; !s.empty; s.popFront())
1601                 put(s.front);
1602         }
1603     }
1604     auto sink = Sink(buf);
1605     auto n = formattedWrite(sink, fmt, args);
1606     version (all)
1607     {
1608         // In the future, this check will be removed to increase consistency
1609         // with formattedWrite
1610         import std.conv : text;
1611         enforceFmt(
1612             n == args.length,
1613             text("Orphan format arguments: args[", n, " .. ", args.length, "]")
1614         );
1615     }
1616     return buf[0 .. sink.i];
1617 }
1618 
1619 /// ditto
1620 char[] sformat(alias fmt, Args...)(char[] buf, Args args)
1621 if (isSomeString!(typeof(fmt)))
1622 {
1623     alias e = checkFormatException!(fmt, Args);
1624     static assert(!e, e);
1625     return .sformat(buf, fmt, args);
1626 }
1627 
1628 ///
1629 @safe pure unittest
1630 {
1631     char[20] buf;
1632     assert(sformat(buf[], "Here are %d %s.", 3, "apples") == "Here are 3 apples.");
1633 
1634     assert(buf[].sformat("Increase: %7.2f %%", 17.4285) == "Increase:   17.43 %");
1635 }
1636 
1637 /// The format string can be checked at compile-time:
1638 @safe pure unittest
1639 {
1640     char[20] buf;
1641 
1642     assert(sformat!"Here are %d %s."(buf[], 3, "apples") == "Here are 3 apples.");
1643 
1644     // This line doesn't compile, because 3.14 cannot be formatted with %d:
1645     // writeln(sformat!"Here are %d %s."(buf[], 3.14, "apples"));
1646 }
1647 
1648 // checking, what is implicitly and explicitly stated in the public unittest
1649 @safe unittest
1650 {
1651     import std.exception : assertThrown;
1652 
1653     char[20] buf;
1654     assertThrown!FormatException(sformat(buf[], "Here are %d %s.", 3.14, "apples"));
1655     assert(!__traits(compiles, sformat!"Here are %d %s."(buf[], 3.14, "apples")));
1656 }
1657 
1658 @safe unittest
1659 {
1660     import core.exception : RangeError;
1661     import std.exception : assertCTFEable, assertThrown;
1662 
1663     assertCTFEable!(
1664     {
1665         char[10] buf;
1666 
1667         assert(sformat(buf[], "foo") == "foo");
1668         assert(sformat(buf[], "foo%%") == "foo%");
1669         assert(sformat(buf[], "foo%s", 'C') == "fooC");
1670         assert(sformat(buf[], "%s foo", "bar") == "bar foo");
1671         () @trusted {
1672             assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc"));
1673         } ();
1674         assert(sformat(buf[], "foo %d", -123) == "foo -123");
1675         assert(sformat(buf[], "foo %d", 123) == "foo 123");
1676 
1677         assertThrown!FormatException(sformat(buf[], "foo %s"));
1678         assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456));
1679 
1680         assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d");
1681     });
1682 }
1683 
1684 @safe unittest // ensure that sformat avoids the GC
1685 {
1686     import core.memory : GC;
1687 
1688     const a = ["foo", "bar"];
1689     const u = () @trusted { return GC.stats().usedSize; } ();
1690     char[20] buf;
1691     sformat(buf, "%d", 123);
1692     sformat(buf, "%s", a);
1693     sformat(buf, "%s", 'c');
1694     const v = () @trusted { return GC.stats().usedSize; } ();
1695     assert(u == v);
1696 }
1697 
1698 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=23488
1699 {
1700     static struct R
1701     {
1702         string s = "Ü";
1703         bool empty() { return s.length == 0; }
1704         char front() { return s[0]; }
1705         void popFront() { s = s[1 .. $]; }
1706     }
1707     char[2] buf;
1708     assert(sformat(buf, "%s", R()) == "Ü");
1709 }
1710 
1711 version (StdUnittest)
1712 private void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__)
1713 {
1714     formatReflectTest(val, fmt, [formatted], fn, ln);
1715 }
1716 
1717 version (StdUnittest)
1718 private void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__)
1719 {
1720     import core.exception : AssertError;
1721     import std.algorithm.searching : canFind;
1722     import std.array : appender;
1723     import std.math.operations : isClose;
1724     import std.traits : FloatingPointTypeOf;
1725 
1726     auto w = appender!string();
1727     formattedWrite(w, fmt, val);
1728 
1729     auto input = w.data;
1730     enforce!AssertError(formatted.canFind(input), input, fn, ln);
1731 
1732     T val2;
1733     formattedRead(input, fmt, val2);
1734 
1735     static if (is(FloatingPointTypeOf!T))
1736         enforce!AssertError(isClose(val, val2), input, fn, ln);
1737     else
1738         enforce!AssertError(val == val2, input, fn, ln);
1739 }
1740 
1741 @safe unittest
1742 {
1743     void booleanTest()
1744     {
1745         auto b = true;
1746         formatReflectTest(b, "%s", `true`);
1747         formatReflectTest(b, "%b", `1`);
1748         formatReflectTest(b, "%o", `1`);
1749         formatReflectTest(b, "%d", `1`);
1750         formatReflectTest(b, "%u", `1`);
1751         formatReflectTest(b, "%x", `1`);
1752     }
1753 
1754     void integerTest()
1755     {
1756         auto n = 127;
1757         formatReflectTest(n, "%s", `127`);
1758         formatReflectTest(n, "%b", `1111111`);
1759         formatReflectTest(n, "%o", `177`);
1760         formatReflectTest(n, "%d", `127`);
1761         formatReflectTest(n, "%u", `127`);
1762         formatReflectTest(n, "%x", `7f`);
1763     }
1764 
1765     void floatingTest()
1766     {
1767         auto f = 3.14;
1768         formatReflectTest(f, "%s", `3.14`);
1769         formatReflectTest(f, "%e", `3.140000e+00`);
1770         formatReflectTest(f, "%f", `3.140000`);
1771         formatReflectTest(f, "%g", `3.14`);
1772     }
1773 
1774     void charTest()
1775     {
1776         auto c = 'a';
1777         formatReflectTest(c, "%s", `a`);
1778         formatReflectTest(c, "%c", `a`);
1779         formatReflectTest(c, "%b", `1100001`);
1780         formatReflectTest(c, "%o", `141`);
1781         formatReflectTest(c, "%d", `97`);
1782         formatReflectTest(c, "%u", `97`);
1783         formatReflectTest(c, "%x", `61`);
1784     }
1785 
1786     void strTest()
1787     {
1788         auto s = "hello";
1789         formatReflectTest(s, "%s",              `hello`);
1790         formatReflectTest(s, "%(%c,%)",         `h,e,l,l,o`);
1791         formatReflectTest(s, "%(%s,%)",         `'h','e','l','l','o'`);
1792         formatReflectTest(s, "[%(<%c>%| $ %)]", `[<h> $ <e> $ <l> $ <l> $ <o>]`);
1793     }
1794 
1795     void daTest()
1796     {
1797         auto a = [1,2,3,4];
1798         formatReflectTest(a, "%s",              `[1, 2, 3, 4]`);
1799         formatReflectTest(a, "[%(%s; %)]",      `[1; 2; 3; 4]`);
1800         formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
1801     }
1802 
1803     void saTest()
1804     {
1805         int[4] sa = [1,2,3,4];
1806         formatReflectTest(sa, "%s",              `[1, 2, 3, 4]`);
1807         formatReflectTest(sa, "[%(%s; %)]",      `[1; 2; 3; 4]`);
1808         formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
1809     }
1810 
1811     void aaTest()
1812     {
1813         auto aa = [1:"hello", 2:"world"];
1814         formatReflectTest(aa, "%s",                    [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]);
1815         formatReflectTest(aa, "[%(%s->%s, %)]",        [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]);
1816         formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]);
1817     }
1818 
1819     import std.exception : assertCTFEable;
1820 
1821     assertCTFEable!(
1822     {
1823         booleanTest();
1824         integerTest();
1825         floatingTest();
1826         charTest();
1827         strTest();
1828         daTest();
1829         saTest();
1830         aaTest();
1831     });
1832 }