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 then the value
216 given by the width parameter, the output is right
217 justified. With the $(B '-') flag this is changed
218 to left justification.
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 then 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.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
this()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 {
putNoOpSink721 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")
unformatElement(T,Range,Char)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);
version(all)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
toStringS1347 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 /// ditto
1360 typeof(fmt) format(alias fmt, Args...)(Args args)
1361 if (isSomeString!(typeof(fmt)))
1362 {
1363 import std.array : appender;
1364 import std.range.primitives : ElementEncodingType;
1365 import std.traits : Unqual;
1366
1367 alias e = checkFormatException!(fmt, Args);
1368 alias Char = Unqual!(ElementEncodingType!(typeof(fmt)));
1369
1370 static assert(!e, e);
1371 auto w = appender!(immutable(Char)[]);
1372
1373 // no need to traverse the string twice during compile time
1374 if (!__ctfe)
1375 {
1376 enum len = guessLength!Char(fmt);
1377 w.reserve(len);
1378 }
1379 else
1380 {
1381 w.reserve(fmt.length);
1382 }
1383
1384 formattedWrite(w, fmt, args);
1385 return w.data;
1386 }
1387
1388 /// The format string can be checked at compile-time:
1389 @safe pure unittest
1390 {
1391 auto s = format!"%s is %s"("Pi", 3.14);
1392 assert(s == "Pi is 3.14");
1393
1394 // This line doesn't compile, because 3.14 cannot be formatted with %d:
1395 // s = format!"%s is %d"("Pi", 3.14);
1396 }
1397
1398 @safe pure unittest
1399 {
1400 string s;
1401 static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg
1402 static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg
1403 static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg
1404 }
1405
1406 // https://issues.dlang.org/show_bug.cgi?id=17381
1407 @safe pure unittest
1408 {
1409 static assert(!__traits(compiles, format!"%s"(1.5, 2)));
1410 static assert(!__traits(compiles, format!"%f"(1.5, 2)));
1411 static assert(!__traits(compiles, format!"%s"(1.5L, 2)));
1412 static assert(!__traits(compiles, format!"%f"(1.5L, 2)));
1413 }
1414
1415 // called during compilation to guess the length of the
1416 // result of format
guessLength(Char,S)1417 private size_t guessLength(Char, S)(S fmtString)
1418 {
1419 import std.array : appender;
1420
1421 size_t len;
1422 auto output = appender!(immutable(Char)[])();
1423 auto spec = FormatSpec!Char(fmtString);
1424 while (spec.writeUpToNextSpec(output))
1425 {
1426 // take a guess
1427 if (spec.width == 0 && (spec.precision == spec.UNSPECIFIED || spec.precision == spec.DYNAMIC))
1428 {
1429 switch (spec.spec)
1430 {
1431 case 'c':
1432 ++len;
1433 break;
1434 case 'd':
1435 case 'x':
1436 case 'X':
1437 len += 3;
1438 break;
1439 case 'b':
1440 len += 8;
1441 break;
1442 case 'f':
1443 case 'F':
1444 len += 10;
1445 break;
1446 case 's':
1447 case 'e':
1448 case 'E':
1449 case 'g':
1450 case 'G':
1451 len += 12;
1452 break;
1453 default: break;
1454 }
1455
1456 continue;
1457 }
1458
1459 if ((spec.spec == 'e' || spec.spec == 'E' || spec.spec == 'g' ||
1460 spec.spec == 'G' || spec.spec == 'f' || spec.spec == 'F') &&
1461 spec.precision != spec.UNSPECIFIED && spec.precision != spec.DYNAMIC &&
1462 spec.width == 0
1463 )
1464 {
1465 len += spec.precision + 5;
1466 continue;
1467 }
1468
1469 if (spec.width == spec.precision)
1470 len += spec.width;
1471 else if (spec.width > 0 && spec.width != spec.DYNAMIC &&
1472 (spec.precision == spec.UNSPECIFIED || spec.width > spec.precision))
1473 {
1474 len += spec.width;
1475 }
1476 else if (spec.precision != spec.UNSPECIFIED && spec.precision > spec.width)
1477 len += spec.precision;
1478 }
1479 len += output.data.length;
1480 return len;
1481 }
1482
1483 @safe pure
1484 unittest
1485 {
1486 assert(guessLength!char("%c") == 1);
1487 assert(guessLength!char("%d") == 3);
1488 assert(guessLength!char("%x") == 3);
1489 assert(guessLength!char("%b") == 8);
1490 assert(guessLength!char("%f") == 10);
1491 assert(guessLength!char("%s") == 12);
1492 assert(guessLength!char("%02d") == 2);
1493 assert(guessLength!char("%02d") == 2);
1494 assert(guessLength!char("%4.4d") == 4);
1495 assert(guessLength!char("%2.4f") == 4);
1496 assert(guessLength!char("%02d:%02d:%02d") == 8);
1497 assert(guessLength!char("%0.2f") == 7);
1498 assert(guessLength!char("%0*d") == 0);
1499 }
1500
1501 /**
1502 Converts its arguments according to a format string into a buffer.
1503 The buffer has to be large enough to hold the formatted string.
1504
1505 The second version of `sformat` takes the format string as a template
1506 argument. In this case, it is checked for consistency at
1507 compile-time.
1508
1509 Params:
1510 buf = the buffer where the formatted string should go
1511 fmt = a $(MREF_ALTTEXT format string, std,format)
1512 args = a variadic list of arguments to be formatted
1513 Char = character type of `fmt`
1514 Args = a variadic list of types of the arguments
1515
1516 Returns:
1517 A slice of `buf` containing the formatted string.
1518
1519 Throws:
1520 A $(REF_ALTTEXT RangeError, RangeError, core, exception) if `buf`
1521 isn't large enough to hold the formatted string
1522 and a $(LREF FormatException) if formatting did not succeed.
1523
1524 Note:
1525 In theory this function should be `@nogc`. But with the current
1526 implementation there are some cases where allocations occur:
1527
1528 $(UL
1529 $(LI An exception is thrown.)
1530 $(LI A custom `toString` function of a compound type allocates.))
1531 */
sformat(Char,Args...)1532 char[] sformat(Char, Args...)(return scope char[] buf, scope const(Char)[] fmt, Args args)
1533 {
1534 import core.exception : RangeError;
1535 import std.range.primitives;
1536 import std.utf : encode;
1537
1538 static struct Sink
1539 {
1540 char[] buf;
1541 size_t i;
1542 void put(dchar c)
1543 {
1544 char[4] enc;
1545 auto n = encode(enc, c);
1546
1547 if (buf.length < i + n)
1548 throw new RangeError(__FILE__, __LINE__);
1549
1550 buf[i .. i + n] = enc[0 .. n];
1551 i += n;
1552 }
1553 void put(scope const(char)[] s)
1554 {
1555 if (buf.length < i + s.length)
1556 throw new RangeError(__FILE__, __LINE__);
1557
1558 buf[i .. i + s.length] = s[];
1559 i += s.length;
1560 }
1561 void put(scope const(wchar)[] s)
1562 {
1563 for (; !s.empty; s.popFront())
1564 put(s.front);
1565 }
1566 void put(scope const(dchar)[] s)
1567 {
1568 for (; !s.empty; s.popFront())
1569 put(s.front);
1570 }
1571 }
1572 auto sink = Sink(buf);
1573 auto n = formattedWrite(sink, fmt, args);
1574 version (all)
1575 {
1576 // In the future, this check will be removed to increase consistency
1577 // with formattedWrite
1578 import std.conv : text;
1579 enforceFmt(
1580 n == args.length,
1581 text("Orphan format arguments: args[", n, " .. ", args.length, "]")
1582 );
1583 }
1584 return buf[0 .. sink.i];
1585 }
1586
1587 /// ditto
1588 char[] sformat(alias fmt, Args...)(char[] buf, Args args)
1589 if (isSomeString!(typeof(fmt)))
1590 {
1591 alias e = checkFormatException!(fmt, Args);
1592 static assert(!e, e);
1593 return .sformat(buf, fmt, args);
1594 }
1595
1596 ///
1597 @safe pure unittest
1598 {
1599 char[20] buf;
1600 assert(sformat(buf[], "Here are %d %s.", 3, "apples") == "Here are 3 apples.");
1601
1602 assert(buf[].sformat("Increase: %7.2f %%", 17.4285) == "Increase: 17.43 %");
1603 }
1604
1605 /// The format string can be checked at compile-time:
1606 @safe pure unittest
1607 {
1608 char[20] buf;
1609
1610 assert(sformat!"Here are %d %s."(buf[], 3, "apples") == "Here are 3 apples.");
1611
1612 // This line doesn't compile, because 3.14 cannot be formatted with %d:
1613 // writeln(sformat!"Here are %d %s."(buf[], 3.14, "apples"));
1614 }
1615
1616 // checking, what is implicitly and explicitly stated in the public unittest
1617 @safe unittest
1618 {
1619 import std.exception : assertThrown;
1620
1621 char[20] buf;
1622 assertThrown!FormatException(sformat(buf[], "Here are %d %s.", 3.14, "apples"));
1623 assert(!__traits(compiles, sformat!"Here are %d %s."(buf[], 3.14, "apples")));
1624 }
1625
1626 @safe unittest
1627 {
1628 import core.exception : RangeError;
1629 import std.exception : assertCTFEable, assertThrown;
1630
1631 assertCTFEable!(
1632 {
1633 char[10] buf;
1634
1635 assert(sformat(buf[], "foo") == "foo");
1636 assert(sformat(buf[], "foo%%") == "foo%");
1637 assert(sformat(buf[], "foo%s", 'C') == "fooC");
1638 assert(sformat(buf[], "%s foo", "bar") == "bar foo");
1639 () @trusted {
1640 assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc"));
1641 } ();
1642 assert(sformat(buf[], "foo %d", -123) == "foo -123");
1643 assert(sformat(buf[], "foo %d", 123) == "foo 123");
1644
1645 assertThrown!FormatException(sformat(buf[], "foo %s"));
1646 assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456));
1647
1648 assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d");
1649 });
1650 }
1651
1652 @safe unittest // ensure that sformat avoids the GC
1653 {
1654 import core.memory : GC;
1655
1656 const a = ["foo", "bar"];
1657 const u = () @trusted { return GC.stats().usedSize; } ();
1658 char[20] buf;
1659 sformat(buf, "%d", 123);
1660 sformat(buf, "%s", a);
1661 sformat(buf, "%s", 'c');
1662 const v = () @trusted { return GC.stats().usedSize; } ();
1663 assert(u == v);
1664 }
1665
version(StdUnittest)1666 version (StdUnittest)
1667 private void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__)
1668 {
1669 formatReflectTest(val, fmt, [formatted], fn, ln);
1670 }
1671
version(StdUnittest)1672 version (StdUnittest)
1673 private void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__)
1674 {
1675 import core.exception : AssertError;
1676 import std.algorithm.searching : canFind;
1677 import std.array : appender;
1678 import std.math.operations : isClose;
1679 import std.traits : FloatingPointTypeOf;
1680
1681 auto w = appender!string();
1682 formattedWrite(w, fmt, val);
1683
1684 auto input = w.data;
1685 enforce!AssertError(formatted.canFind(input), input, fn, ln);
1686
1687 T val2;
1688 formattedRead(input, fmt, val2);
1689
1690 static if (is(FloatingPointTypeOf!T))
1691 enforce!AssertError(isClose(val, val2), input, fn, ln);
1692 else
1693 enforce!AssertError(val == val2, input, fn, ln);
1694 }
1695
1696 @safe unittest
1697 {
booleanTest()1698 void booleanTest()
1699 {
1700 auto b = true;
1701 formatReflectTest(b, "%s", `true`);
1702 formatReflectTest(b, "%b", `1`);
1703 formatReflectTest(b, "%o", `1`);
1704 formatReflectTest(b, "%d", `1`);
1705 formatReflectTest(b, "%u", `1`);
1706 formatReflectTest(b, "%x", `1`);
1707 }
1708
integerTest()1709 void integerTest()
1710 {
1711 auto n = 127;
1712 formatReflectTest(n, "%s", `127`);
1713 formatReflectTest(n, "%b", `1111111`);
1714 formatReflectTest(n, "%o", `177`);
1715 formatReflectTest(n, "%d", `127`);
1716 formatReflectTest(n, "%u", `127`);
1717 formatReflectTest(n, "%x", `7f`);
1718 }
1719
floatingTest()1720 void floatingTest()
1721 {
1722 auto f = 3.14;
1723 formatReflectTest(f, "%s", `3.14`);
1724 formatReflectTest(f, "%e", `3.140000e+00`);
1725 formatReflectTest(f, "%f", `3.140000`);
1726 formatReflectTest(f, "%g", `3.14`);
1727 }
1728
charTest()1729 void charTest()
1730 {
1731 auto c = 'a';
1732 formatReflectTest(c, "%s", `a`);
1733 formatReflectTest(c, "%c", `a`);
1734 formatReflectTest(c, "%b", `1100001`);
1735 formatReflectTest(c, "%o", `141`);
1736 formatReflectTest(c, "%d", `97`);
1737 formatReflectTest(c, "%u", `97`);
1738 formatReflectTest(c, "%x", `61`);
1739 }
1740
strTest()1741 void strTest()
1742 {
1743 auto s = "hello";
1744 formatReflectTest(s, "%s", `hello`);
1745 formatReflectTest(s, "%(%c,%)", `h,e,l,l,o`);
1746 formatReflectTest(s, "%(%s,%)", `'h','e','l','l','o'`);
1747 formatReflectTest(s, "[%(<%c>%| $ %)]", `[<h> $ <e> $ <l> $ <l> $ <o>]`);
1748 }
1749
daTest()1750 void daTest()
1751 {
1752 auto a = [1,2,3,4];
1753 formatReflectTest(a, "%s", `[1, 2, 3, 4]`);
1754 formatReflectTest(a, "[%(%s; %)]", `[1; 2; 3; 4]`);
1755 formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
1756 }
1757
saTest()1758 void saTest()
1759 {
1760 int[4] sa = [1,2,3,4];
1761 formatReflectTest(sa, "%s", `[1, 2, 3, 4]`);
1762 formatReflectTest(sa, "[%(%s; %)]", `[1; 2; 3; 4]`);
1763 formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
1764 }
1765
aaTest()1766 void aaTest()
1767 {
1768 auto aa = [1:"hello", 2:"world"];
1769 formatReflectTest(aa, "%s", [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]);
1770 formatReflectTest(aa, "[%(%s->%s, %)]", [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]);
1771 formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]);
1772 }
1773
1774 import std.exception : assertCTFEable;
1775
1776 assertCTFEable!(
1777 {
1778 booleanTest();
1779 integerTest();
1780 floatingTest();
1781 charTest();
1782 strTest();
1783 daTest();
1784 saTest();
1785 aaTest();
1786 });
1787 }
1788