1 /******************************************************************************* 2 3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Initial release: January 2006 8 9 author: Kris 10 11 *******************************************************************************/ 12 13 module tango.io.stream.Lines; 14 15 private import tango.io.model.IConduit; 16 private import tango.io.stream.Iterator; 17 18 /******************************************************************************* 19 20 Iterate across a set of text patterns. 21 22 Each pattern is exposed to the client as a slice of the original 23 content, where the slice is transient. If you need to retain the 24 exposed content, then you should .dup it appropriately. 25 26 The content exposed via an iterator is supposed to be entirely 27 read-only. All current iterators abide by this rule, but it is 28 possible a user could mutate the content through a get() slice. 29 To enforce the desired read-only aspect, the code would have to 30 introduce redundant copying or the compiler would have to support 31 read-only arrays. 32 33 See Delimiters, Patterns, Quotes. 34 35 *******************************************************************************/ 36 37 class Lines(T) : Iterator!(T) 38 { 39 /*********************************************************************** 40 41 Construct an uninitialized iterator. For example: 42 --- 43 auto lines = new Lines!(char); 44 45 void somefunc (InputStream stream) 46 { 47 foreach (line; lines.set(stream)) 48 Cout (line).newline; 49 } 50 --- 51 52 Construct a streaming iterator upon a stream: 53 --- 54 void somefunc (InputStream stream) 55 { 56 foreach (line; new Lines!(char) (stream)) 57 Cout (line).newline; 58 } 59 --- 60 61 Construct a streaming iterator upon a conduit: 62 --- 63 foreach (line; new Lines!(char) (new File ("myfile"))) 64 Cout (line).newline; 65 --- 66 67 ***********************************************************************/ 68 69 this (InputStream stream = null) 70 { 71 super (stream); 72 } 73 74 /*********************************************************************** 75 76 Read a line of text, and return false when there's no 77 further content available. 78 79 ***********************************************************************/ 80 81 final bool readln (ref const(T)[] content) 82 { 83 content = super.next; 84 return content.ptr !is null; 85 } 86 87 /*********************************************************************** 88 89 Scanner implementation for this iterator. Find a '\n', 90 and eat any immediately preceeding '\r'. 91 92 ***********************************************************************/ 93 94 protected override size_t scan (const(void)[] data) 95 { 96 auto content = (cast(const(T)*) data.ptr) [0 .. data.length / T.sizeof]; 97 98 foreach (i, T c; content) 99 if (c is '\n') 100 { 101 auto slice = i; 102 if (i && content[i-1] is '\r') 103 --slice; 104 set (content.ptr, 0, slice, i); 105 return found (i); 106 } 107 108 return notFound(); 109 } 110 } 111 112 113 114 /******************************************************************************* 115 116 *******************************************************************************/ 117 118 debug(UnitTest) 119 { 120 private import tango.io.device.Array; 121 122 unittest 123 { 124 auto p = new Lines!(char) (new Array("blah".dup)); 125 } 126 } 127 128 129 /******************************************************************************* 130 131 *******************************************************************************/ 132 133 debug (Lines) 134 { 135 import tango.io.Console; 136 import tango.io.device.Array; 137 138 void main() 139 { 140 auto lines = new Lines!(char)(new Array("one\ntwo\r\nthree".dup)); 141 foreach (i, line, delim; lines) 142 Cout (line) (delim); 143 } 144 }