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 }