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 307 alias e = checkFormatException!(fmt, Args); 308 static assert(!e, e); 309 return .formattedRead(r, fmt, args); 310 } 311 312 /// 313 @safe pure unittest 314 { 315 string object; 316 char cmp; 317 int value; 318 319 assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3); 320 assert(object == "angle"); 321 assert(cmp == '<'); 322 assert(value == 36); 323 324 // reading may end early: 325 assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2); 326 assert(object == "length"); 327 assert(cmp == '>'); 328 // value is not changed: 329 assert(value == 36); 330 } 331 332 /// The format string can be checked at compile-time: 333 @safe pure unittest 334 { 335 string a; 336 int b; 337 double c; 338 339 assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3); 340 assert(a == "hello"); 341 assert(b == 124); 342 assert(c == 34.5); 343 } 344 345 /// Skipping values 346 @safe pure unittest 347 { 348 string item; 349 double amount; 350 351 assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2); 352 assert(item == "orange"); 353 assert(amount == 15.25); 354 355 // can also be used with tuples 356 import std.typecons : Tuple; 357 358 Tuple!(int, float) t; 359 char[] line = "1 7643 2.125".dup; 360 formattedRead(line, "%s %*u %s", t); 361 assert(t[0] == 1 && t[1] == 2.125); 362 } 363 364 @safe unittest 365 { 366 import std.math.operations : isClose; 367 import std.math.traits : isNaN; 368 import std.range.primitives : empty; 369 370 string s = " 1.2 3.4 "; 371 double x, y, z; 372 assert(formattedRead(s, " %s %s %s ", x, y, z) == 2); 373 assert(s.empty); 374 assert(isClose(x, 1.2)); 375 assert(isClose(y, 3.4)); 376 assert(isNaN(z)); 377 } 378 379 // for backwards compatibility 380 @safe pure unittest 381 { 382 string s = "hello!124:34.5"; 383 string a; 384 int b; 385 double c; 386 formattedRead(s, "%s!%s:%s", &a, &b, &c); 387 assert(a == "hello" && b == 124 && c == 34.5); 388 389 // mix pointers and auto-ref 390 s = "world!200:42.25"; 391 formattedRead(s, "%s!%s:%s", a, &b, &c); 392 assert(a == "world" && b == 200 && c == 42.25); 393 394 s = "world1!201:42.5"; 395 formattedRead(s, "%s!%s:%s", &a, &b, c); 396 assert(a == "world1" && b == 201 && c == 42.5); 397 398 s = "world2!202:42.75"; 399 formattedRead(s, "%s!%s:%s", a, b, &c); 400 assert(a == "world2" && b == 202 && c == 42.75); 401 } 402 403 // for backwards compatibility 404 @safe pure 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 @safe unittest 420 { 421 string s = "hello!124:34.5"; 422 string a; 423 int b; 424 double c; 425 formattedRead(s, "%s!%s:%s", &a, &b, &c); 426 assert(a == "hello" && b == 124 && c == 34.5); 427 } 428 429 @safe pure unittest 430 { 431 string line; 432 433 bool f1; 434 435 line = "true"; 436 formattedRead(line, "%s", &f1); 437 assert(f1); 438 439 line = "TrUE"; 440 formattedRead(line, "%s", &f1); 441 assert(f1); 442 443 line = "false"; 444 formattedRead(line, "%s", &f1); 445 assert(!f1); 446 447 line = "fALsE"; 448 formattedRead(line, "%s", &f1); 449 assert(!f1); 450 451 line = "1"; 452 formattedRead(line, "%d", &f1); 453 assert(f1); 454 455 line = "-1"; 456 formattedRead(line, "%d", &f1); 457 assert(f1); 458 459 line = "0"; 460 formattedRead(line, "%d", &f1); 461 assert(!f1); 462 463 line = "-0"; 464 formattedRead(line, "%d", &f1); 465 assert(!f1); 466 } 467 468 @safe pure unittest 469 { 470 union B 471 { 472 char[int.sizeof] untyped; 473 int typed; 474 } 475 476 B b; 477 b.typed = 5; 478 char[] input = b.untyped[]; 479 int witness; 480 formattedRead(input, "%r", &witness); 481 assert(witness == b.typed); 482 } 483 484 @safe pure unittest 485 { 486 union A 487 { 488 char[float.sizeof] untyped; 489 float typed; 490 } 491 492 A a; 493 a.typed = 5.5; 494 char[] input = a.untyped[]; 495 float witness; 496 formattedRead(input, "%r", &witness); 497 assert(witness == a.typed); 498 } 499 500 @safe pure unittest 501 { 502 import std.typecons : Tuple; 503 504 char[] line = "1 2".dup; 505 int a, b; 506 formattedRead(line, "%s %s", &a, &b); 507 assert(a == 1 && b == 2); 508 509 line = "10 2 3".dup; 510 formattedRead(line, "%d ", &a); 511 assert(a == 10); 512 assert(line == "2 3"); 513 514 Tuple!(int, float) t; 515 line = "1 2.125".dup; 516 formattedRead(line, "%d %g", &t); 517 assert(t[0] == 1 && t[1] == 2.125); 518 519 line = "1 7643 2.125".dup; 520 formattedRead(line, "%s %*u %s", &t); 521 assert(t[0] == 1 && t[1] == 2.125); 522 } 523 524 @safe pure unittest 525 { 526 string line; 527 528 char c1, c2; 529 530 line = "abc"; 531 formattedRead(line, "%s%c", &c1, &c2); 532 assert(c1 == 'a' && c2 == 'b'); 533 assert(line == "c"); 534 } 535 536 @safe pure unittest 537 { 538 string line; 539 540 line = "[1,2,3]"; 541 int[] s1; 542 formattedRead(line, "%s", &s1); 543 assert(s1 == [1,2,3]); 544 } 545 546 @safe pure unittest 547 { 548 string line; 549 550 line = "[1,2,3]"; 551 int[] s1; 552 formattedRead(line, "[%(%s,%)]", &s1); 553 assert(s1 == [1,2,3]); 554 555 line = `["hello", "world"]`; 556 string[] s2; 557 formattedRead(line, "[%(%s, %)]", &s2); 558 assert(s2 == ["hello", "world"]); 559 560 line = "123 456"; 561 int[] s3; 562 formattedRead(line, "%(%s %)", &s3); 563 assert(s3 == [123, 456]); 564 565 line = "h,e,l,l,o; w,o,r,l,d"; 566 string[] s4; 567 formattedRead(line, "%(%(%c,%); %)", &s4); 568 assert(s4 == ["hello", "world"]); 569 } 570 571 @safe pure unittest 572 { 573 import std.exception : assertThrown; 574 575 string line; 576 577 int[4] sa1; 578 line = `[1,2,3,4]`; 579 formattedRead(line, "%s", &sa1); 580 assert(sa1 == [1,2,3,4]); 581 582 int[4] sa2; 583 line = `[1,2,3]`; 584 assertThrown(formattedRead(line, "%s", &sa2)); 585 586 int[4] sa3; 587 line = `[1,2,3,4,5]`; 588 assertThrown(formattedRead(line, "%s", &sa3)); 589 } 590 591 @safe pure unittest 592 { 593 import std.exception : assertThrown; 594 import std.format : FormatException; 595 596 string input; 597 598 int[4] sa1; 599 input = `[1,2,3,4]`; 600 formattedRead(input, "[%(%s,%)]", &sa1); 601 assert(sa1 == [1,2,3,4]); 602 603 int[4] sa2; 604 input = `[1,2,3]`; 605 assertThrown!FormatException(formattedRead(input, "[%(%s,%)]", &sa2)); 606 } 607 608 @safe pure unittest 609 { 610 string line; 611 612 string s1, s2; 613 614 line = "hello, world"; 615 formattedRead(line, "%s", &s1); 616 assert(s1 == "hello, world", s1); 617 618 line = "hello, world;yah"; 619 formattedRead(line, "%s;%s", &s1, &s2); 620 assert(s1 == "hello, world", s1); 621 assert(s2 == "yah", s2); 622 623 line = `['h','e','l','l','o']`; 624 string s3; 625 formattedRead(line, "[%(%s,%)]", &s3); 626 assert(s3 == "hello"); 627 628 line = `"hello"`; 629 string s4; 630 formattedRead(line, "\"%(%c%)\"", &s4); 631 assert(s4 == "hello"); 632 } 633 634 @safe pure unittest 635 { 636 string line; 637 638 string[int] aa1; 639 line = `[1:"hello", 2:"world"]`; 640 formattedRead(line, "%s", &aa1); 641 assert(aa1 == [1:"hello", 2:"world"]); 642 643 int[string] aa2; 644 line = `{"hello"=1; "world"=2}`; 645 formattedRead(line, "{%(%s=%s; %)}", &aa2); 646 assert(aa2 == ["hello":1, "world":2]); 647 648 int[string] aa3; 649 line = `{[hello=1]; [world=2]}`; 650 formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3); 651 assert(aa3 == ["hello":1, "world":2]); 652 } 653 654 // test rvalue using 655 @safe pure unittest 656 { 657 string[int] aa1; 658 formattedRead!("%s")(`[1:"hello", 2:"world"]`, aa1); 659 assert(aa1 == [1:"hello", 2:"world"]); 660 661 int[string] aa2; 662 formattedRead(`{"hello"=1; "world"=2}`, "{%(%s=%s; %)}", aa2); 663 assert(aa2 == ["hello":1, "world":2]); 664 } 665 666 /** 667 Reads a value from the given _input range and converts it according to a 668 format specifier. 669 670 Params: 671 input = the $(REF_ALTTEXT input range, isInputRange, std, range, primitives), 672 to read from 673 spec = a $(MREF_ALTTEXT format string, std,format) 674 T = type to return 675 Range = the type of the input range `input` 676 Char = the character type used for `spec` 677 678 Returns: 679 A value from `input` of type `T`. 680 681 Throws: 682 A $(REF_ALTTEXT FormatException, FormatException, std, format) 683 if reading did not succeed. 684 685 See_Also: 686 $(REF parse, std, conv) and $(REF to, std, conv) 687 */ 688 T unformatValue(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) 689 { 690 return unformatValueImpl!T(input, spec); 691 } 692 693 /// 694 @safe pure unittest 695 { 696 import std.format.spec : singleSpec; 697 698 string s = "42"; 699 auto spec = singleSpec("%s"); 700 assert(unformatValue!int(s, spec) == 42); 701 } 702 703 // https://issues.dlang.org/show_bug.cgi?id=7241 704 @safe pure unittest 705 { 706 string input = "a"; 707 auto spec = FormatSpec!char("%s"); 708 spec.readUpToNextSpec(input); 709 auto result = unformatValue!(dchar[1])(input, spec); 710 assert(result[0] == 'a'); 711 } 712 713 // https://issues.dlang.org/show_bug.cgi?id=20393 714 @safe pure unittest 715 { 716 import std.exception : assertThrown; 717 string str = "foo 12a-buzz"; 718 string a, c; 719 int b; 720 assertThrown(formattedRead(str, "%s %d-%s", &a, &b, &c)); 721 } 722 723 // https://issues.dlang.org/show_bug.cgi?id=18051 724 @safe pure unittest 725 { 726 import std.format : format; 727 728 enum Op { lt, gt, eq } 729 730 auto s = format!"%s"(Op.lt); 731 Op op; 732 assert(formattedRead!"%s"(s, op) == 1); 733 assert(op == Op.lt); 734 } 735