1 // Written in the D programming language.
2 
3 /**
4 This is a submodule of $(MREF std, format).
5 
6 It provides two functions for reading formatted input: $(LREF
7 unformatValue) and $(LREF formattedRead). The former reads a single
8 value. The latter reads several values at once and matches the
9 characters found between format specifiers.
10 
11 Parameters are ignored, except for the ones consisting of a single
12 $(B '*'). See $(LREF formattedRead) for more information.
13 
14 A space outside of a format specifier has a special meaning: it
15 matches any sequence of whitespace characters, not just a single
16 space.
17 
18 The following combinations of format characters and types are
19 available:
20 
21 $(BOOKTABLE ,
22 $(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o, x, X) $(TH e, E, f, g, G) $(TH r) $(TH compound))
23 $(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
24 $(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
25 $(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
26 $(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)))
27 $(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
28 $(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
29 $(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
30 $(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
31 )
32 
33 Below are highlighted examples on how these combinations are used
34 with $(LREF unformatValue), however, they apply for $(LREF
35 formattedRead) also
36 
37 Copyright: Copyright The D Language Foundation 2000-2013.
38 
39 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
40 
41 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
42 Andrei Alexandrescu), and Kenji Hara
43 
44 Source: $(PHOBOSSRC std/format/read.d)
45  */
46 module std.format.read;
47 
48 /// Booleans
49 @safe pure unittest
50 {
51     import std.format.spec : singleSpec;
52 
53     auto str = "false";
54     auto spec = singleSpec("%s");
55     assert(str.unformatValue!bool(spec) == false);
56 
57     str = "1";
58     spec = singleSpec("%d");
59     assert(str.unformatValue!bool(spec) == true);
60 }
61 
62 /// Null values
63 @safe pure unittest
64 {
65     import std.format.spec : singleSpec;
66 
67     auto str = "null";
68     auto spec = singleSpec("%s");
69     assert(str.unformatValue!(typeof(null))(spec) == null);
70 }
71 
72 /// Integrals
73 @safe pure unittest
74 {
75     import std.format.spec : singleSpec;
76 
77     // signed decimal values
78     auto str = "123";
79     auto spec = singleSpec("%s");
80     assert(str.unformatValue!int(spec) == 123);
81 
82     // hexadecimal values
83     str = "ABC";
84     spec = singleSpec("%X");
85     assert(str.unformatValue!int(spec) == 2748);
86 
87     // octal values
88     str = "11610";
89     spec = singleSpec("%o");
90     assert(str.unformatValue!int(spec) == 5000);
91 
92     // raw read, depends on endianess
93     str = "\x75\x01";
94     spec = singleSpec("%r");
95     auto result = str.unformatValue!short(spec);
96     assert(result == 373 /* little endian */ || result == 29953 /* big endian */ );
97 }
98 
99 /// Floating point numbers
100 @safe pure unittest
101 {
102     import std.format.spec : singleSpec;
103     import std.math.operations : isClose;
104 
105     // natural notation
106     auto str = "123.456";
107     auto spec = singleSpec("%s");
108     assert(str.unformatValue!double(spec).isClose(123.456));
109 
110     // scientific notation
111     str = "1e17";
112     spec = singleSpec("%e");
113     assert(str.unformatValue!double(spec).isClose(1e17));
114 
115     // raw read, depends on endianess
116     str = "\x40\x00\x00\xBF";
117     spec = singleSpec("%r");
118     auto result = str.unformatValue!float(spec);
119     assert(isClose(result, -0.5) /* little endian */ || isClose(result, 2.0) /* big endian */ );
120 }
121 
122 /// Characters
123 @safe pure unittest
124 {
125     import std.format.spec : singleSpec;
126 
127     // only the first character is read
128     auto str = "abc";
129     auto spec = singleSpec("%s");
130     assert(str.unformatValue!char(spec) == 'a');
131 
132     // using a numerical format character treats the read number as unicode code point
133     str = "65";
134     spec = singleSpec("%d");
135     assert(str.unformatValue!char(spec) == 'A');
136 
137     str = "41";
138     spec = singleSpec("%x");
139     assert(str.unformatValue!char(spec) == 'A');
140 
141     str = "10003";
142     spec = singleSpec("%d");
143     assert(str.unformatValue!dchar(spec) == '✓');
144 }
145 
146 /// Arrays
147 @safe pure unittest
148 {
149     import std.format.spec : singleSpec;
150 
151     // string value
152     string str = "aaa";
153     auto spec = singleSpec("%s");
154     assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
155 
156     // fixed size array with characters
157     str = "aaa";
158     spec = singleSpec("%s");
159     dchar[3] ret = ['a', 'a', 'a'];
160     assert(str.unformatValue!(dchar[3])(spec) == ret);
161 
162     // dynamic array
163     str = "[1, 2, 3, 4]";
164     spec = singleSpec("%s");
165     assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
166 
167     // fixed size array with integers
168     str = "[1, 2, 3, 4]";
169     spec = singleSpec("%s");
170     int[4] ret2 = [1, 2, 3, 4];
171     assert(str.unformatValue!(int[4])(spec) == ret2);
172 
173     // compound specifiers can be used for more control
174     str = "1,2,3";
175     spec = singleSpec("%(%s,%)");
176     assert(str.unformatValue!(int[])(spec) == [1, 2, 3]);
177 
178     str = "cool";
179     spec = singleSpec("%(%c%)");
180     assert(str.unformatValue!(char[])(spec) == ['c', 'o', 'o', 'l']);
181 }
182 
183 /// Associative arrays
184 @safe pure unittest
185 {
186     import std.format.spec : singleSpec;
187 
188     // as single value
189     auto str = `["one": 1, "two": 2]`;
190     auto spec = singleSpec("%s");
191     assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]);
192 
193     // with compound specifier for more control
194     str = "1/1, 2/4, 3/9";
195     spec = singleSpec("%(%d/%d%|, %)");
196     assert(str.unformatValue!(int[int])(spec) == [1: 1, 2: 4, 3: 9]);
197 }
198 
199 import std.format.spec : FormatSpec;
200 import std.format.internal.read;
201 import std.meta : allSatisfy;
202 import std.traits : isSomeString, isType;
203 
204 /**
205 Reads an input range according to a format string and stores the read
206 values into its arguments.
207 
208 Format specifiers with format character $(B 'd'), $(B 'u') and $(B
209 'c') can take a $(B '*') parameter for skipping values.
210 
211 The second version of `formattedRead` takes the format string as
212 template argument. In this case, it is checked for consistency at
213 compile-time.
214 
215 Params:
216     r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
217         where the formatted input is read from
218     fmt = a $(MREF_ALTTEXT format string, std,format)
219     args = a variadic list of arguments where the read values are stored
220     Range = the type of the input range `r`
221     Char = the character type used for `fmt`
222     Args = a variadic list of types of the arguments
223 
224 Returns:
225     The number of variables filled. If the input range `r` ends early,
226     this number will be less than the number of variables provided.
227 
228 Throws:
229     A $(REF_ALTTEXT FormatException, FormatException, std, format)
230     if reading did not succeed.
231 
232 Note:
233     For backward compatibility the arguments `args` can be given as pointers
234     to that variable, but it is not recommended to do so, because this
235     option might be removed in the future.
236  */
237 uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, auto ref Args args)
238 {
239     import std.format : enforceFmt;
240     import std.range.primitives : empty;
241     import std.traits : isPointer;
242     import std.typecons : isTuple;
243 
244     auto spec = FormatSpec!Char(fmt);
245     static if (!Args.length)
246     {
247         spec.readUpToNextSpec(r);
248         enforceFmt(spec.trailing.empty, "Trailing characters in formattedRead format string");
249         return 0;
250     }
251     else
252     {
253         enum hasPointer = isPointer!(typeof(args[0]));
254 
255         // The function below accounts for '*' == fields meant to be
256         // read and skipped
257         void skipUnstoredFields()
258         {
259             for (;;)
260             {
261                 spec.readUpToNextSpec(r);
262                 if (spec.width != spec.DYNAMIC) break;
263                 // must skip this field
264                 skipData(r, spec);
265             }
266         }
267 
268         skipUnstoredFields();
269         if (r.empty)
270         {
271             // Input is empty, nothing to read
272             return 0;
273         }
274 
275         static if (hasPointer)
276             alias A = typeof(*args[0]);
277         else
278             alias A = typeof(args[0]);
279 
280         static if (isTuple!A)
281         {
282             foreach (i, T; A.Types)
283             {
284                 static if (hasPointer)
285                     (*args[0])[i] = unformatValue!(T)(r, spec);
286                 else
287                     args[0][i] = unformatValue!(T)(r, spec);
288                 skipUnstoredFields();
289             }
290         }
291         else
292         {
293             static if (hasPointer)
294                 *args[0] = unformatValue!(A)(r, spec);
295             else
296                 args[0] = unformatValue!(A)(r, spec);
297         }
298         return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
299     }
300 }
301 
302 /// ditto
303 uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
304 if (!isType!fmt && isSomeString!(typeof(fmt)))
305 {
306     import std.format : checkFormatException;
307     import std.meta : staticMap;
308     import std.typecons : Tuple;
309 
310 
311     // formattedRead supports std.typecons.Tuple
312     // however, checkFormatException does not
313     // this means that all std.typecons.Tuple's types in Args must be unwrapped
314     // and passed to checkFormatException
315     template Flatten(T)
316     {
317         static if (is(T : Tuple!Args, Args...))
318             alias Flatten = Args;
319         else
320             alias Flatten = T;
321     }
322 
323     alias e = checkFormatException!(fmt, staticMap!(Flatten, Args));
324     static assert(!e, e);
325     return .formattedRead(r, fmt, args);
326 }
327 
328 ///
329 @safe pure unittest
330 {
331     string object;
332     char cmp;
333     int value;
334 
335     assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3);
336     assert(object == "angle");
337     assert(cmp == '<');
338     assert(value == 36);
339 
340     // reading may end early:
341     assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2);
342     assert(object == "length");
343     assert(cmp == '>');
344     // value is not changed:
345     assert(value == 36);
346 }
347 
348 /// The format string can be checked at compile-time:
349 @safe pure unittest
350 {
351     string a;
352     int b;
353     double c;
354 
355     assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3);
356     assert(a == "hello");
357     assert(b == 124);
358     assert(c == 34.5);
359 }
360 
361 /// Skipping values
362 @safe pure unittest
363 {
364     string item;
365     double amount;
366 
367     assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2);
368     assert(item == "orange");
369     assert(amount == 15.25);
370 
371     // can also be used with tuples
372     import std.typecons : Tuple;
373 
374     Tuple!(int, float) t;
375     char[] line = "1 7643 2.125".dup;
376     formattedRead(line, "%s %*u %s", t);
377     assert(t[0] == 1 && t[1] == 2.125);
378 }
379 
380 @safe pure unittest
381 {
382     string hello;
383     string world;
384 
385     assert("hello ignore world".formattedRead("%s %*s %s", hello, world) == 2);
386     assert(hello == "hello");
387     assert(world == "world");
388 }
389 
390 // https://issues.dlang.org/show_bug.cgi?id=23600
391 @safe pure unittest
392 {
393     import std.typecons : Tuple, tuple;
394 
395     string h, w;
396     Tuple!(int, float) t;
397 
398     assert("hello 1 2.34 world".formattedRead!"%s %d %f %s"(h, t, w) == 3);
399     assert(h == "hello");
400     assert(t == tuple(1, 2.34f));
401     assert(w == "world");
402 }
403 
404 @safe unittest
405 {
406     import std.math.operations : isClose;
407     import std.math.traits : isNaN;
408     import std.range.primitives : empty;
409 
410     string s = " 1.2 3.4 ";
411     double x, y, z;
412     assert(formattedRead(s, " %s %s %s ", x, y, z) == 2);
413     assert(s.empty);
414     assert(isClose(x, 1.2));
415     assert(isClose(y, 3.4));
416     assert(isNaN(z));
417 }
418 
419 // for backwards compatibility
420 @safe pure unittest
421 {
422     string s = "hello!124:34.5";
423     string a;
424     int b;
425     double c;
426     formattedRead(s, "%s!%s:%s", &a, &b, &c);
427     assert(a == "hello" && b == 124 && c == 34.5);
428 
429     // mix pointers and auto-ref
430     s = "world!200:42.25";
431     formattedRead(s, "%s!%s:%s", a, &b, &c);
432     assert(a == "world" && b == 200 && c == 42.25);
433 
434     s = "world1!201:42.5";
435     formattedRead(s, "%s!%s:%s", &a, &b, c);
436     assert(a == "world1" && b == 201 && c == 42.5);
437 
438     s = "world2!202:42.75";
439     formattedRead(s, "%s!%s:%s", a, b, &c);
440     assert(a == "world2" && b == 202 && c == 42.75);
441 }
442 
443 // for backwards compatibility
444 @safe pure unittest
445 {
446     import std.math.operations : isClose;
447     import std.math.traits : isNaN;
448     import std.range.primitives : empty;
449 
450     string s = " 1.2 3.4 ";
451     double x, y, z;
452     assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2);
453     assert(s.empty);
454     assert(isClose(x, 1.2));
455     assert(isClose(y, 3.4));
456     assert(isNaN(z));
457 }
458 
459 @safe unittest
460 {
461     string s = "hello!124:34.5";
462     string a;
463     int b;
464     double c;
465     formattedRead(s, "%s!%s:%s", &a, &b, &c);
466     assert(a == "hello" && b == 124 && c == 34.5);
467 }
468 
469 @safe pure unittest
470 {
471     string line;
472 
473     bool f1;
474 
475     line = "true";
476     formattedRead(line, "%s", &f1);
477     assert(f1);
478 
479     line = "TrUE";
480     formattedRead(line, "%s", &f1);
481     assert(f1);
482 
483     line = "false";
484     formattedRead(line, "%s", &f1);
485     assert(!f1);
486 
487     line = "fALsE";
488     formattedRead(line, "%s", &f1);
489     assert(!f1);
490 
491     line = "1";
492     formattedRead(line, "%d", &f1);
493     assert(f1);
494 
495     line = "-1";
496     formattedRead(line, "%d", &f1);
497     assert(f1);
498 
499     line = "0";
500     formattedRead(line, "%d", &f1);
501     assert(!f1);
502 
503     line = "-0";
504     formattedRead(line, "%d", &f1);
505     assert(!f1);
506 }
507 
508 @safe pure unittest
509 {
510     union B
511     {
512         char[int.sizeof] untyped;
513         int typed;
514     }
515 
516     B b;
517     b.typed = 5;
518     char[] input = b.untyped[];
519     int witness;
520     formattedRead(input, "%r", &witness);
521     assert(witness == b.typed);
522 }
523 
524 @safe pure unittest
525 {
526     union A
527     {
528         char[float.sizeof] untyped;
529         float typed;
530     }
531 
532     A a;
533     a.typed = 5.5;
534     char[] input = a.untyped[];
535     float witness;
536     formattedRead(input, "%r", &witness);
537     assert(witness == a.typed);
538 }
539 
540 @safe pure unittest
541 {
542     import std.typecons : Tuple;
543 
544     char[] line = "1 2".dup;
545     int a, b;
546     formattedRead(line, "%s %s", &a, &b);
547     assert(a == 1 && b == 2);
548 
549     line = "10 2 3".dup;
550     formattedRead(line, "%d ", &a);
551     assert(a == 10);
552     assert(line == "2 3");
553 
554     Tuple!(int, float) t;
555     line = "1 2.125".dup;
556     formattedRead(line, "%d %g", &t);
557     assert(t[0] == 1 && t[1] == 2.125);
558 
559     line = "1 7643 2.125".dup;
560     formattedRead(line, "%s %*u %s", &t);
561     assert(t[0] == 1 && t[1] == 2.125);
562 }
563 
564 @safe pure unittest
565 {
566     string line;
567 
568     char c1, c2;
569 
570     line = "abc";
571     formattedRead(line, "%s%c", &c1, &c2);
572     assert(c1 == 'a' && c2 == 'b');
573     assert(line == "c");
574 }
575 
576 @safe pure unittest
577 {
578     string line;
579 
580     line = "[1,2,3]";
581     int[] s1;
582     formattedRead(line, "%s", &s1);
583     assert(s1 == [1,2,3]);
584 }
585 
586 @safe pure unittest
587 {
588     string line;
589 
590     line = "[1,2,3]";
591     int[] s1;
592     formattedRead(line, "[%(%s,%)]", &s1);
593     assert(s1 == [1,2,3]);
594 
595     line = `["hello", "world"]`;
596     string[] s2;
597     formattedRead(line, "[%(%s, %)]", &s2);
598     assert(s2 == ["hello", "world"]);
599 
600     line = "123 456";
601     int[] s3;
602     formattedRead(line, "%(%s %)", &s3);
603     assert(s3 == [123, 456]);
604 
605     line = "h,e,l,l,o; w,o,r,l,d";
606     string[] s4;
607     formattedRead(line, "%(%(%c,%); %)", &s4);
608     assert(s4 == ["hello", "world"]);
609 }
610 
611 @safe pure unittest
612 {
613     import std.exception : assertThrown;
614 
615     string line;
616 
617     int[4] sa1;
618     line = `[1,2,3,4]`;
619     formattedRead(line, "%s", &sa1);
620     assert(sa1 == [1,2,3,4]);
621 
622     int[4] sa2;
623     line = `[1,2,3]`;
624     assertThrown(formattedRead(line, "%s", &sa2));
625 
626     int[4] sa3;
627     line = `[1,2,3,4,5]`;
628     assertThrown(formattedRead(line, "%s", &sa3));
629 }
630 
631 @safe pure unittest
632 {
633     import std.exception : assertThrown;
634     import std.format : FormatException;
635 
636     string input;
637 
638     int[4] sa1;
639     input = `[1,2,3,4]`;
640     formattedRead(input, "[%(%s,%)]", &sa1);
641     assert(sa1 == [1,2,3,4]);
642 
643     int[4] sa2;
644     input = `[1,2,3]`;
645     assertThrown!FormatException(formattedRead(input, "[%(%s,%)]", &sa2));
646 }
647 
648 @safe pure unittest
649 {
650     string line;
651 
652     string s1, s2;
653 
654     line = "hello, world";
655     formattedRead(line, "%s", &s1);
656     assert(s1 == "hello, world", s1);
657 
658     line = "hello, world;yah";
659     formattedRead(line, "%s;%s", &s1, &s2);
660     assert(s1 == "hello, world", s1);
661     assert(s2 == "yah", s2);
662 
663     line = `['h','e','l','l','o']`;
664     string s3;
665     formattedRead(line, "[%(%s,%)]", &s3);
666     assert(s3 == "hello");
667 
668     line = `"hello"`;
669     string s4;
670     formattedRead(line, "\"%(%c%)\"", &s4);
671     assert(s4 == "hello");
672 }
673 
674 @safe pure unittest
675 {
676     string line;
677 
678     string[int] aa1;
679     line = `[1:"hello", 2:"world"]`;
680     formattedRead(line, "%s", &aa1);
681     assert(aa1 == [1:"hello", 2:"world"]);
682 
683     int[string] aa2;
684     line = `{"hello"=1; "world"=2}`;
685     formattedRead(line, "{%(%s=%s; %)}", &aa2);
686     assert(aa2 == ["hello":1, "world":2]);
687 
688     int[string] aa3;
689     line = `{[hello=1]; [world=2]}`;
690     formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3);
691     assert(aa3 == ["hello":1, "world":2]);
692 }
693 
694 // test rvalue using
695 @safe pure unittest
696 {
697     string[int] aa1;
698     formattedRead!("%s")(`[1:"hello", 2:"world"]`, aa1);
699     assert(aa1 == [1:"hello", 2:"world"]);
700 
701     int[string] aa2;
702     formattedRead(`{"hello"=1; "world"=2}`, "{%(%s=%s; %)}", aa2);
703     assert(aa2 == ["hello":1, "world":2]);
704 }
705 
706 /**
707 Reads an input range according to a format string and returns a tuple of Args
708 with the read values.
709 
710 Format specifiers with format character $(B 'd'), $(B 'u') and $(B
711 'c') can take a $(B '*') parameter for skipping values.
712 
713 The second version of `formattedRead` takes the format string as
714 template argument. In this case, it is checked for consistency at
715 compile-time.
716 
717 Params:
718     Args = a variadic list of types of the arguments
719  */
720 template formattedRead(Args...)
721 if (Args.length && allSatisfy!(isType, Args))
722 {
723     import std.typecons : Tuple;
724 
725     /**
726     Params:
727         r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
728             where the formatted input is read from
729         fmt = a $(MREF_ALTTEXT format string, std,format)
730         Range = the type of the input range `r`
731         Char = the character type used for `fmt`
732 
733     Returns:
734         A Tuple!Args with the elements filled.
735 
736     Throws:
737         A $(REF_ALTTEXT FormatException, FormatException, std, format)
738         if reading did not succeed.
739     */
740     Tuple!Args formattedRead(Range, Char)(auto ref Range r, const(Char)[] fmt)
741     {
742         import core.lifetime : forward;
743         import std.format : enforceFmt;
744 
745         Tuple!Args args;
746         const numArgsFilled = .formattedRead(forward!r, fmt, args.expand);
747         enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments");
748         return args;
749     }
750 }
751 
752 ///
753 @safe pure unittest
754 {
755     import std.exception : assertThrown;
756     import std.format : FormatException;
757     import std.typecons : tuple;
758 
759     auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s");
760     assert(complete == tuple("hello", 34.5, 124));
761 
762     // reading ends early
763     assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s"));
764 }
765 
766 /// Skipping values
767 @safe pure unittest
768 {
769     import std.format : FormatException;
770     import std.typecons : tuple;
771 
772     auto result = "orange: (12%) 15.25".formattedRead!(string, double)("%s: (%*d%%) %f");
773     assert(result == tuple("orange", 15.25));
774 }
775 
776 /// ditto
777 template formattedRead(alias fmt, Args...)
778 if (!isType!fmt && isSomeString!(typeof(fmt)) && Args.length && allSatisfy!(isType, Args))
779 {
780     import std.typecons : Flag, Tuple, Yes;
781     Tuple!Args formattedRead(Range)(auto ref Range r)
782     {
783         import core.lifetime : forward;
784         import std.format : enforceFmt;
785 
786         Tuple!Args args;
787         const numArgsFilled = .formattedRead!fmt(forward!r, args.expand);
788         enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments");
789         return args;
790     }
791 }
792 
793 /// The format string can be checked at compile-time
794 @safe pure unittest
795 {
796     import std.exception : assertThrown;
797     import std.format : FormatException;
798     import std.typecons : tuple;
799 
800     auto expected = tuple("hello", 124, 34.5);
801     auto result = "hello!124:34.5".formattedRead!("%s!%s:%s", string, int, double);
802     assert(result == expected);
803 
804     assertThrown!FormatException("hello!34.5:".formattedRead!("%s!%s:%s", string, double, int));
805 }
806 
807 /// Compile-time consistency check
808 @safe pure unittest
809 {
810     import std.format : FormatException;
811     import std.typecons : tuple;
812 
813     static assert(!__traits(compiles, "orange: (12%) 15.25".formattedRead!("%s: (%*d%%) %f", string, double)));
814 }
815 
816 /**
817 Reads a value from the given _input range and converts it according to a
818 format specifier.
819 
820 Params:
821     input = the $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
822             to read from
823     spec = a $(MREF_ALTTEXT format string, std,format)
824     T = type to return
825     Range = the type of the input range `input`
826     Char = the character type used for `spec`
827 
828 Returns:
829     A value from `input` of type `T`.
830 
831 Throws:
832     A $(REF_ALTTEXT FormatException, FormatException, std, format)
833     if reading did not succeed.
834 
835 See_Also:
836     $(REF parse, std, conv) and $(REF to, std, conv)
837  */
838 T unformatValue(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
839 {
840     return unformatValueImpl!T(input, spec);
841 }
842 
843 ///
844 @safe pure unittest
845 {
846     import std.format.spec : singleSpec;
847 
848     string s = "42";
849     auto spec = singleSpec("%s");
850     assert(unformatValue!int(s, spec) == 42);
851 }
852 
853 // https://issues.dlang.org/show_bug.cgi?id=7241
854 @safe pure unittest
855 {
856     string input = "a";
857     auto spec = FormatSpec!char("%s");
858     spec.readUpToNextSpec(input);
859     auto result = unformatValue!(dchar[1])(input, spec);
860     assert(result[0] == 'a');
861 }
862 
863 // https://issues.dlang.org/show_bug.cgi?id=20393
864 @safe pure unittest
865 {
866     import std.exception : assertThrown;
867     string str = "foo 12a-buzz";
868     string a, c;
869     int b;
870     assertThrown(formattedRead(str, "%s %d-%s", &a, &b, &c));
871 }
872 
873 // https://issues.dlang.org/show_bug.cgi?id=18051
874 @safe pure unittest
875 {
876     import std.format : format;
877 
878     enum Op { lt, gt, eq }
879 
880     auto s = format!"%s"(Op.lt);
881     Op op;
882     assert(formattedRead!"%s"(s, op) == 1);
883     assert(op == Op.lt);
884 }