1 module dkh.scanner; 2 3 import dkh.container.stackpayload; 4 5 /** 6 Scanner 速くはないが遅くもない printf/scanfよりちょっと遅いくらい? 7 */ 8 class Scanner { 9 import std.stdio : File; 10 import std.conv : to; 11 import std.range : front, popFront, array, ElementType; 12 import std.array : split; 13 import std.traits : isSomeChar, isStaticArray, isArray; 14 import std.algorithm : map; 15 File f; 16 this(File f) { 17 this.f = f; 18 } 19 char[512] lineBuf; 20 char[] line; 21 private bool succW() { 22 import std.range.primitives : empty, front, popFront; 23 import std.ascii : isWhite; 24 while (!line.empty && line.front.isWhite) { 25 line.popFront; 26 } 27 return !line.empty; 28 } 29 private bool succ() { 30 import std.range.primitives : empty, front, popFront; 31 import std.ascii : isWhite; 32 while (true) { 33 while (!line.empty && line.front.isWhite) { 34 line.popFront; 35 } 36 if (!line.empty) break; 37 line = lineBuf[]; 38 f.readln(line); 39 if (!line.length) return false; 40 } 41 return true; 42 } 43 44 private bool readSingle(T)(ref T x) { 45 import std.algorithm : findSplitBefore; 46 import std.string : strip; 47 import std.conv : parse; 48 if (!succ()) return false; 49 static if (isArray!T) { 50 alias E = ElementType!T; 51 static if (isSomeChar!E) { 52 //string or char[10] etc 53 //todo optimize 54 auto r = line.findSplitBefore(" "); 55 x = r[0].strip.dup; 56 line = r[1]; 57 } else static if (isStaticArray!T) { 58 foreach (i; 0..T.length) { 59 bool f = succW(); 60 assert(f); 61 x[i] = line.parse!E; 62 } 63 } else { 64 StackPayload!E buf; 65 while (succW()) { 66 buf ~= line.parse!E; 67 } 68 x = buf.data; 69 } 70 } else { 71 x = line.parse!T; 72 } 73 return true; 74 } 75 int read(T, Args...)(ref T x, auto ref Args args) { 76 if (!readSingle(x)) return 0; 77 static if (args.length == 0) { 78 return 1; 79 } else { 80 return 1 + read(args); 81 } 82 } 83 } 84 85 86 /// 87 unittest { 88 import std.path : buildPath; 89 import std.file : tempDir; 90 import std.algorithm : equal; 91 import std.stdio : File; 92 string fileName = buildPath(tempDir, "kyuridenanmaida.txt"); 93 auto fout = File(fileName, "w"); 94 fout.writeln("1 2 3"); 95 fout.writeln("ab cde"); 96 fout.writeln("1.0 1.0 2.0"); 97 fout.close; 98 Scanner sc = new Scanner(File(fileName, "r")); 99 int a; 100 int[2] b; 101 char[2] c; 102 string d; 103 double e; 104 double[] f; 105 sc.read(a, b, c, d, e, f); 106 assert(a == 1); 107 assert(equal(b[], [2, 3])); // 配列型は行末まで読み込む 108 assert(equal(c[], "ab")); // char型配列はトークンをそのまま返す 109 assert(equal(d, "cde")); // stringもchar型配列と同様 110 assert(e == 1.0); // 小数も可 111 assert(equal(f, [1.0, 2.0])); 112 assert(sc.read(a) == 0); // EOF 113 } 114 115 unittest { 116 import std.path : buildPath; 117 import std.file : tempDir; 118 import std.algorithm : equal; 119 import std.stdio : File, writeln; 120 import dkh.stopwatch; 121 string fileName = buildPath(tempDir, "kyuridenanmaida.txt"); 122 auto fout = File(fileName, "w"); 123 foreach (i; 0..1_000_000) { 124 fout.writeln(3*i, " ", 3*i+1, " ", 3*i+2); 125 } 126 fout.close; 127 StopWatch sw; 128 sw.start; 129 Scanner sc = new Scanner(File(fileName, "r")); 130 foreach (i; 0..500_000) { 131 int a, b, c; 132 sc.read(a, b, c); 133 assert(a == 3*i); 134 assert(b == 3*i+1); 135 assert(c == 3*i+2); 136 } 137 foreach (i; 500_000..700_000) { 138 int[3] d; 139 sc.read(d); 140 int a = d[0], b = d[1], c = d[2]; 141 assert(a == 3*i); 142 assert(b == 3*i+1); 143 assert(c == 3*i+2); 144 } 145 foreach (i; 700_000..1_000_000) { 146 int[] d; 147 sc.read(d); 148 assert(d.length == 3); 149 int a = d[0], b = d[1], c = d[2]; 150 assert(a == 3*i); 151 assert(b == 3*i+1); 152 assert(c == 3*i+2); 153 } 154 writeln("Scanner Speed Test(3*1,000,000 int): ", sw.peek.toMsecs); 155 }