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.traits : isSomeString;
202
203 /**
204 Reads an input range according to a format string and stores the read
205 values into its arguments.
206
207 Format specifiers with format character $(B 'd'), $(B 'u') and $(B
208 'c') can take a $(B '*') parameter for skipping values.
209
210 The second version of `formattedRead` takes the format string as
211 template argument. In this case, it is checked for consistency at
212 compile-time.
213
214 Params:
215 r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
216 where the formatted input is read from
217 fmt = a $(MREF_ALTTEXT format string, std,format)
218 args = a variadic list of arguments where the read values are stored
219 Range = the type of the input range `r`
220 Char = the character type used for `fmt`
221 Args = a variadic list of types of the arguments
222
223 Returns:
224 The number of variables filled. If the input range `r` ends early,
225 this number will be less than the number of variables provided.
226
227 Throws:
228 A $(REF_ALTTEXT FormatException, FormatException, std, format)
229 if reading did not succeed.
230
231 Note:
232 For backward compatibility the arguments `args` can be given as pointers
233 to that variable, but it is not recommended to do so, because this
234 option might be removed in the future.
235 */
236 uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, auto ref Args args)
237 {
238 import std.format : enforceFmt;
239 import std.range.primitives : empty;
240 import std.traits : isPointer;
241 import std.typecons : isTuple;
242
243 auto spec = FormatSpec!Char(fmt);
244 static if (!Args.length)
245 {
246 spec.readUpToNextSpec(r);
247 enforceFmt(spec.trailing.empty, "Trailing characters in formattedRead format string");
248 return 0;
249 }
250 else
251 {
252 enum hasPointer = isPointer!(typeof(args[0]));
253
254 // The function below accounts for '*' == fields meant to be
255 // read and skipped
256 void skipUnstoredFields()
257 {
258 for (;;)
259 {
260 spec.readUpToNextSpec(r);
261 if (spec.width != spec.DYNAMIC) break;
262 // must skip this field
263 skipData(r, spec);
264 }
265 }
266
267 skipUnstoredFields();
268 if (r.empty)
269 {
270 // Input is empty, nothing to read
271 return 0;
272 }
273
274 static if (hasPointer)
275 alias A = typeof(*args[0]);
276 else
277 alias A = typeof(args[0]);
278
279 static if (isTuple!A)
280 {
281 foreach (i, T; A.Types)
282 {
283 static if (hasPointer)
284 (*args[0])[i] = unformatValue!(T)(r, spec);
285 else
286 args[0][i] = unformatValue!(T)(r, spec);
287 skipUnstoredFields();
288 }
289 }
290 else
291 {
292 static if (hasPointer)
293 *args[0] = unformatValue!(A)(r, spec);
294 else
295 args[0] = unformatValue!(A)(r, spec);
296 }
297 return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
298 }
299 }
300
301 /// ditto
302 uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
303 if (isSomeString!(typeof(fmt)))
304 {
305 import std.format : checkFormatException;
306 import std.meta : staticMap;
307 import std.typecons : Tuple;
308
309
310 // formattedRead supports std.typecons.Tuple
311 // however, checkFormatException does not
312 // this means that all std.typecons.Tuple's types in Args must be unwrapped
313 // and passed to checkFormatException
314 template Flatten(T)
315 {
316 static if (is(T : Tuple!Args, Args...))
317 alias Flatten = Args;
318 else
319 alias Flatten = T;
320 }
321
322 alias e = checkFormatException!(fmt, staticMap!(Flatten, Args));
323 static assert(!e, e);
324 return .formattedRead(r, fmt, args);
325 }
326
327 ///
328 @safe pure unittest
329 {
330 string object;
331 char cmp;
332 int value;
333
334 assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3);
335 assert(object == "angle");
336 assert(cmp == '<');
337 assert(value == 36);
338
339 // reading may end early:
340 assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2);
341 assert(object == "length");
342 assert(cmp == '>');
343 // value is not changed:
344 assert(value == 36);
345 }
346
347 /// The format string can be checked at compile-time:
348 @safe pure unittest
349 {
350 string a;
351 int b;
352 double c;
353
354 assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3);
355 assert(a == "hello");
356 assert(b == 124);
357 assert(c == 34.5);
358 }
359
360 /// Skipping values
361 @safe pure unittest
362 {
363 string item;
364 double amount;
365
366 assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2);
367 assert(item == "orange");
368 assert(amount == 15.25);
369
370 // can also be used with tuples
371 import std.typecons : Tuple;
372
373 Tuple!(int, float) t;
374 char[] line = "1 7643 2.125".dup;
375 formattedRead(line, "%s %*u %s", t);
376 assert(t[0] == 1 && t[1] == 2.125);
377 }
378
379 // https://issues.dlang.org/show_bug.cgi?id=23600
380 @safe pure unittest
381 {
382 import std.typecons : Tuple, tuple;
383
384 string h, w;
385 Tuple!(int, float) t;
386
387 assert("hello 1 2.34 world".formattedRead!"%s %d %f %s"(h, t, w) == 3);
388 assert(h == "hello");
389 assert(t == tuple(1, 2.34f));
390 assert(w == "world");
391 }
392
393 @safe unittest
394 {
395 import std.math.operations : isClose;
396 import std.math.traits : isNaN;
397 import std.range.primitives : empty;
398
399 string s = " 1.2 3.4 ";
400 double x, y, z;
401 assert(formattedRead(s, " %s %s %s ", x, y, z) == 2);
402 assert(s.empty);
403 assert(isClose(x, 1.2));
404 assert(isClose(y, 3.4));
405 assert(isNaN(z));
406 }
407
408 // for backwards compatibility
409 @safe pure unittest
410 {
411 string s = "hello!124:34.5";
412 string a;
413 int b;
414 double c;
415 formattedRead(s, "%s!%s:%s", &a, &b, &c);
416 assert(a == "hello" && b == 124 && c == 34.5);
417
418 // mix pointers and auto-ref
419 s = "world!200:42.25";
420 formattedRead(s, "%s!%s:%s", a, &b, &c);
421 assert(a == "world" && b == 200 && c == 42.25);
422
423 s = "world1!201:42.5";
424 formattedRead(s, "%s!%s:%s", &a, &b, c);
425 assert(a == "world1" && b == 201 && c == 42.5);
426
427 s = "world2!202:42.75";
428 formattedRead(s, "%s!%s:%s", a, b, &c);
429 assert(a == "world2" && b == 202 && c == 42.75);
430 }
431
432 // for backwards compatibility
433 @safe pure unittest
434 {
435 import std.math.operations : isClose;
436 import std.math.traits : isNaN;
437 import std.range.primitives : empty;
438
439 string s = " 1.2 3.4 ";
440 double x, y, z;
441 assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2);
442 assert(s.empty);
443 assert(isClose(x, 1.2));
444 assert(isClose(y, 3.4));
445 assert(isNaN(z));
446 }
447
448 @safe unittest
449 {
450 string s = "hello!124:34.5";
451 string a;
452 int b;
453 double c;
454 formattedRead(s, "%s!%s:%s", &a, &b, &c);
455 assert(a == "hello" && b == 124 && c == 34.5);
456 }
457
458 @safe pure unittest
459 {
460 string line;
461
462 bool f1;
463
464 line = "true";
465 formattedRead(line, "%s", &f1);
466 assert(f1);
467
468 line = "TrUE";
469 formattedRead(line, "%s", &f1);
470 assert(f1);
471
472 line = "false";
473 formattedRead(line, "%s", &f1);
474 assert(!f1);
475
476 line = "fALsE";
477 formattedRead(line, "%s", &f1);
478 assert(!f1);
479
480 line = "1";
481 formattedRead(line, "%d", &f1);
482 assert(f1);
483
484 line = "-1";
485 formattedRead(line, "%d", &f1);
486 assert(f1);
487
488 line = "0";
489 formattedRead(line, "%d", &f1);
490 assert(!f1);
491
492 line = "-0";
493 formattedRead(line, "%d", &f1);
494 assert(!f1);
495 }
496
497 @safe pure unittest
498 {
499 union B
500 {
501 char[int.sizeof] untyped;
502 int typed;
503 }
504
505 B b;
506 b.typed = 5;
507 char[] input = b.untyped[];
508 int witness;
509 formattedRead(input, "%r", &witness);
510 assert(witness == b.typed);
511 }
512
513 @safe pure unittest
514 {
515 union A
516 {
517 char[float.sizeof] untyped;
518 float typed;
519 }
520
521 A a;
522 a.typed = 5.5;
523 char[] input = a.untyped[];
524 float witness;
525 formattedRead(input, "%r", &witness);
526 assert(witness == a.typed);
527 }
528
529 @safe pure unittest
530 {
531 import std.typecons : Tuple;
532
533 char[] line = "1 2".dup;
534 int a, b;
535 formattedRead(line, "%s %s", &a, &b);
536 assert(a == 1 && b == 2);
537
538 line = "10 2 3".dup;
539 formattedRead(line, "%d ", &a);
540 assert(a == 10);
541 assert(line == "2 3");
542
543 Tuple!(int, float) t;
544 line = "1 2.125".dup;
545 formattedRead(line, "%d %g", &t);
546 assert(t[0] == 1 && t[1] == 2.125);
547
548 line = "1 7643 2.125".dup;
549 formattedRead(line, "%s %*u %s", &t);
550 assert(t[0] == 1 && t[1] == 2.125);
551 }
552
553 @safe pure unittest
554 {
555 string line;
556
557 char c1, c2;
558
559 line = "abc";
560 formattedRead(line, "%s%c", &c1, &c2);
561 assert(c1 == 'a' && c2 == 'b');
562 assert(line == "c");
563 }
564
565 @safe pure unittest
566 {
567 string line;
568
569 line = "[1,2,3]";
570 int[] s1;
571 formattedRead(line, "%s", &s1);
572 assert(s1 == [1,2,3]);
573 }
574
575 @safe pure unittest
576 {
577 string line;
578
579 line = "[1,2,3]";
580 int[] s1;
581 formattedRead(line, "[%(%s,%)]", &s1);
582 assert(s1 == [1,2,3]);
583
584 line = `["hello", "world"]`;
585 string[] s2;
586 formattedRead(line, "[%(%s, %)]", &s2);
587 assert(s2 == ["hello", "world"]);
588
589 line = "123 456";
590 int[] s3;
591 formattedRead(line, "%(%s %)", &s3);
592 assert(s3 == [123, 456]);
593
594 line = "h,e,l,l,o; w,o,r,l,d";
595 string[] s4;
596 formattedRead(line, "%(%(%c,%); %)", &s4);
597 assert(s4 == ["hello", "world"]);
598 }
599
600 @safe pure unittest
601 {
602 import std.exception : assertThrown;
603
604 string line;
605
606 int[4] sa1;
607 line = `[1,2,3,4]`;
608 formattedRead(line, "%s", &sa1);
609 assert(sa1 == [1,2,3,4]);
610
611 int[4] sa2;
612 line = `[1,2,3]`;
613 assertThrown(formattedRead(line, "%s", &sa2));
614
615 int[4] sa3;
616 line = `[1,2,3,4,5]`;
617 assertThrown(formattedRead(line, "%s", &sa3));
618 }
619
620 @safe pure unittest
621 {
622 import std.exception : assertThrown;
623 import std.format : FormatException;
624
625 string input;
626
627 int[4] sa1;
628 input = `[1,2,3,4]`;
629 formattedRead(input, "[%(%s,%)]", &sa1);
630 assert(sa1 == [1,2,3,4]);
631
632 int[4] sa2;
633 input = `[1,2,3]`;
634 assertThrown!FormatException(formattedRead(input, "[%(%s,%)]", &sa2));
635 }
636
637 @safe pure unittest
638 {
639 string line;
640
641 string s1, s2;
642
643 line = "hello, world";
644 formattedRead(line, "%s", &s1);
645 assert(s1 == "hello, world", s1);
646
647 line = "hello, world;yah";
648 formattedRead(line, "%s;%s", &s1, &s2);
649 assert(s1 == "hello, world", s1);
650 assert(s2 == "yah", s2);
651
652 line = `['h','e','l','l','o']`;
653 string s3;
654 formattedRead(line, "[%(%s,%)]", &s3);
655 assert(s3 == "hello");
656
657 line = `"hello"`;
658 string s4;
659 formattedRead(line, "\"%(%c%)\"", &s4);
660 assert(s4 == "hello");
661 }
662
663 @safe pure unittest
664 {
665 string line;
666
667 string[int] aa1;
668 line = `[1:"hello", 2:"world"]`;
669 formattedRead(line, "%s", &aa1);
670 assert(aa1 == [1:"hello", 2:"world"]);
671
672 int[string] aa2;
673 line = `{"hello"=1; "world"=2}`;
674 formattedRead(line, "{%(%s=%s; %)}", &aa2);
675 assert(aa2 == ["hello":1, "world":2]);
676
677 int[string] aa3;
678 line = `{[hello=1]; [world=2]}`;
679 formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3);
680 assert(aa3 == ["hello":1, "world":2]);
681 }
682
683 // test rvalue using
684 @safe pure unittest
685 {
686 string[int] aa1;
687 formattedRead!("%s")(`[1:"hello", 2:"world"]`, aa1);
688 assert(aa1 == [1:"hello", 2:"world"]);
689
690 int[string] aa2;
691 formattedRead(`{"hello"=1; "world"=2}`, "{%(%s=%s; %)}", aa2);
692 assert(aa2 == ["hello":1, "world":2]);
693 }
694
695 /**
696 Reads a value from the given _input range and converts it according to a
697 format specifier.
698
699 Params:
700 input = the $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
701 to read from
702 spec = a $(MREF_ALTTEXT format string, std,format)
703 T = type to return
704 Range = the type of the input range `input`
705 Char = the character type used for `spec`
706
707 Returns:
708 A value from `input` of type `T`.
709
710 Throws:
711 A $(REF_ALTTEXT FormatException, FormatException, std, format)
712 if reading did not succeed.
713
714 See_Also:
715 $(REF parse, std, conv) and $(REF to, std, conv)
716 */
717 T unformatValue(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
718 {
719 return unformatValueImpl!T(input, spec);
720 }
721
722 ///
723 @safe pure unittest
724 {
725 import std.format.spec : singleSpec;
726
727 string s = "42";
728 auto spec = singleSpec("%s");
729 assert(unformatValue!int(s, spec) == 42);
730 }
731
732 // https://issues.dlang.org/show_bug.cgi?id=7241
733 @safe pure unittest
734 {
735 string input = "a";
736 auto spec = FormatSpec!char("%s");
737 spec.readUpToNextSpec(input);
738 auto result = unformatValue!(dchar[1])(input, spec);
739 assert(result[0] == 'a');
740 }
741
742 // https://issues.dlang.org/show_bug.cgi?id=20393
743 @safe pure unittest
744 {
745 import std.exception : assertThrown;
746 string str = "foo 12a-buzz";
747 string a, c;
748 int b;
749 assertThrown(formattedRead(str, "%s %d-%s", &a, &b, &c));
750 }
751
752 // https://issues.dlang.org/show_bug.cgi?id=18051
753 @safe pure unittest
754 {
755 import std.format : format;
756
757 enum Op { lt, gt, eq }
758
759 auto s = format!"%s"(Op.lt);
760 Op op;
761 assert(formattedRead!"%s"(s, op) == 1);
762 assert(op == Op.lt);
763 }