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 private File f; 16 /// f is source file 17 this(File f) { 18 this.f = f; 19 } 20 private char[512] lineBuf; 21 private char[] line; 22 private bool succW() { 23 import std.range.primitives : empty, front, popFront; 24 import std.ascii : isWhite; 25 while (!line.empty && line.front.isWhite) { 26 line.popFront; 27 } 28 return !line.empty; 29 } 30 private bool succ() { 31 import std.range.primitives : empty, front, popFront; 32 import std.ascii : isWhite; 33 while (true) { 34 while (!line.empty && line.front.isWhite) { 35 line.popFront; 36 } 37 if (!line.empty) break; 38 line = lineBuf[]; 39 f.readln(line); 40 if (!line.length) return false; 41 } 42 return true; 43 } 44 45 private bool readSingle(T)(ref T x) { 46 import std.algorithm : findSplitBefore; 47 import std..string : strip; 48 import std.conv : parse; 49 if (!succ()) return false; 50 static if (isArray!T) { 51 alias E = ElementType!T; 52 static if (isSomeChar!E) { 53 //string or char[10] etc 54 //todo optimize 55 auto r = line.findSplitBefore(" "); 56 x = r[0].strip.dup; 57 line = r[1]; 58 } else static if (isStaticArray!T) { 59 foreach (i; 0..T.length) { 60 assert(succW()); 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 /// Read tokens. Return the number of success read 77 int unsafeRead(T, Args...)(ref T x, auto ref Args args) { 78 if (!readSingle(x)) return 0; 79 static if (args.length == 0) { 80 return 1; 81 } else { 82 return 1 + read(args); 83 } 84 } 85 /// Read tokens. If can't read, throw exception. 86 void read(Args...)(auto ref Args args) { 87 import std.exception : enforce; 88 static if (args.length != 0) { 89 enforce(readSingle(args[0])); 90 read(args[1..$]); 91 } 92 } 93 /// Do file has next token? 94 bool hasNext() { 95 return succ(); 96 } 97 } 98 99 100 /// 101 unittest { 102 import std.path : buildPath; 103 import std.file : tempDir; 104 import std.algorithm : equal; 105 import std.stdio : File; 106 string fileName = buildPath(tempDir, "kyuridenanmaida.txt"); 107 auto fout = File(fileName, "w"); 108 fout.writeln("1 2 3"); 109 fout.writeln("ab cde"); 110 fout.writeln("1.0 1.0 2.0"); 111 fout.close; 112 Scanner sc = new Scanner(File(fileName, "r")); 113 int a; 114 int[2] b; 115 char[2] c; 116 string d; 117 double e; 118 double[] f; 119 sc.read(a, b, c, d, e, f); 120 assert(a == 1); 121 assert(equal(b[], [2, 3])); // array(except char) read whole line 122 assert(equal(c[], "ab")); // char array(char[], char[2], ...) return token 123 assert(equal(d, "cde")); // string is char array 124 assert(e == 1.0); 125 assert(equal(f, [1.0, 2.0])); 126 assert(!sc.hasNext); 127 } 128 129 unittest { 130 import std.path : buildPath; 131 import std.file : tempDir; 132 import std.algorithm : equal; 133 import std.stdio : File, writeln; 134 import dkh.stopwatch; 135 string fileName = buildPath(tempDir, "kyuridenanmaida.txt"); 136 auto fout = File(fileName, "w"); 137 foreach (i; 0..1_000_000) { 138 fout.writeln(3*i, " ", 3*i+1, " ", 3*i+2); 139 } 140 fout.close; 141 StopWatch sw; 142 sw.start; 143 Scanner sc = new Scanner(File(fileName, "r")); 144 foreach (i; 0..500_000) { 145 int a, b, c; 146 sc.read(a, b, c); 147 assert(a == 3*i); 148 assert(b == 3*i+1); 149 assert(c == 3*i+2); 150 } 151 foreach (i; 500_000..700_000) { 152 int[3] d; 153 sc.read(d); 154 int a = d[0], b = d[1], c = d[2]; 155 assert(a == 3*i); 156 assert(b == 3*i+1); 157 assert(c == 3*i+2); 158 } 159 foreach (i; 700_000..1_000_000) { 160 int[] d; 161 sc.read(d); 162 assert(d.length == 3); 163 int a = d[0], b = d[1], c = d[2]; 164 assert(a == 3*i); 165 assert(b == 3*i+1); 166 assert(c == 3*i+2); 167 } 168 writeln("Scanner Speed Test(3*1,000,000 int): ", sw.peek.toMsecs); 169 }