1 module dkh.scanner; 2 3 import dkh.container.stackpayload; 4 5 /** 6 Scanner, not slow, not fast. 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 76 int unsafeRead(T, Args...)(ref T x, auto ref Args args) { 77 if (!readSingle(x)) return 0; 78 static if (args.length == 0) { 79 return 1; 80 } else { 81 return 1 + read(args); 82 } 83 } 84 void read(Args...)(auto ref Args args) { 85 import std.exception; 86 static if (args.length != 0) { 87 enforce(readSingle(args[0])); 88 read(args[1..$]); 89 } 90 } 91 bool hasNext() { 92 return succ(); 93 } 94 } 95 96 97 /// 98 unittest { 99 import std.path : buildPath; 100 import std.file : tempDir; 101 import std.algorithm : equal; 102 import std.stdio : File; 103 string fileName = buildPath(tempDir, "kyuridenanmaida.txt"); 104 auto fout = File(fileName, "w"); 105 fout.writeln("1 2 3"); 106 fout.writeln("ab cde"); 107 fout.writeln("1.0 1.0 2.0"); 108 fout.close; 109 Scanner sc = new Scanner(File(fileName, "r")); 110 int a; 111 int[2] b; 112 char[2] c; 113 string d; 114 double e; 115 double[] f; 116 sc.read(a, b, c, d, e, f); 117 assert(a == 1); 118 assert(equal(b[], [2, 3])); // 配列型は行末まで読み込む 119 assert(equal(c[], "ab")); // char型配列はトークンをそのまま返す 120 assert(equal(d, "cde")); // stringもchar型配列と同様 121 assert(e == 1.0); // 小数も可 122 assert(equal(f, [1.0, 2.0])); 123 assert(!sc.hasNext); 124 } 125 126 unittest { 127 import std.path : buildPath; 128 import std.file : tempDir; 129 import std.algorithm : equal; 130 import std.stdio : File, writeln; 131 import dkh.stopwatch; 132 string fileName = buildPath(tempDir, "kyuridenanmaida.txt"); 133 auto fout = File(fileName, "w"); 134 foreach (i; 0..1_000_000) { 135 fout.writeln(3*i, " ", 3*i+1, " ", 3*i+2); 136 } 137 fout.close; 138 StopWatch sw; 139 sw.start; 140 Scanner sc = new Scanner(File(fileName, "r")); 141 foreach (i; 0..500_000) { 142 int a, b, c; 143 sc.read(a, b, c); 144 assert(a == 3*i); 145 assert(b == 3*i+1); 146 assert(c == 3*i+2); 147 } 148 foreach (i; 500_000..700_000) { 149 int[3] d; 150 sc.read(d); 151 int a = d[0], b = d[1], c = d[2]; 152 assert(a == 3*i); 153 assert(b == 3*i+1); 154 assert(c == 3*i+2); 155 } 156 foreach (i; 700_000..1_000_000) { 157 int[] d; 158 sc.read(d); 159 assert(d.length == 3); 160 int a = d[0], b = d[1], c = d[2]; 161 assert(a == 3*i); 162 assert(b == 3*i+1); 163 assert(c == 3*i+2); 164 } 165 writeln("Scanner Speed Test(3*1,000,000 int): ", sw.peek.toMsecs); 166 }