make readtxt2 work with pipes
[lcore.git] / readtxt2.pas
1 { Copyright (C) 2009 Bas Steendijk and Peter Green\r
2   For conditions of distribution and use, see copyright notice in zlib_license.txt\r
3   which is included in the package\r
4   ----------------------------------------------------------------------------- }\r
5 \r
6 unit readtxt2;\r
7 \r
8 interface\r
9 \r
10 {\r
11 readtxt, version 2\r
12 by beware\r
13 \r
14 this can be used to read a text file exposed as a tstream line by line.\r
15 automatic handling of CR, LF, and CRLF line endings, and readout of detected line ending type.\r
16 fast: 1.5-2 times faster than textfile readln in tests.\r
17 }\r
18 \r
19 uses\r
20   classes,sysutils;\r
21 \r
22 const\r
23   bufsize=4096;\r
24   eoltype_none=0;\r
25   eoltype_cr=1;\r
26   eoltype_lf=2;\r
27   eoltype_crlf=3;\r
28 \r
29 type\r
30   treadtxt=class(tobject)\r
31   public\r
32     sourcestream:tstream;\r
33     destroysourcestream:boolean;\r
34     constructor create(asourcestream: tstream; adestroysourcestream:boolean);\r
35     constructor createf(filename : string);\r
36 \r
37     function readline:ansistring;\r
38     function eof:boolean;\r
39     destructor destroy; override;\r
40   private\r
41     buf:array[0..bufsize-1] of byte;\r
42     numread:integer;\r
43     bufpointer:integer;\r
44     currenteol,preveol:integer;\r
45     fileeof,reachedeof:boolean;\r
46     eoltype:integer;\r
47     procedure checkandread;\r
48   end;\r
49 \r
50 implementation\r
51 \r
52 constructor treadtxt.create(asourcestream: tstream; adestroysourcestream:boolean);\r
53 begin\r
54   inherited create;\r
55   sourcestream := asourcestream;\r
56   destroysourcestream := adestroysourcestream;\r
57 \r
58   //if sourcestream.Position >= sourcestream.size then fileeof := true;\r
59   bufpointer := bufsize;\r
60 end;\r
61 \r
62 constructor treadtxt.createf(filename: string);\r
63 begin\r
64   create(tfilestream.create(filename,fmOpenRead),true);\r
65 end;\r
66 \r
67 \r
68 procedure treadtxt.checkandread;\r
69 begin\r
70   if bufpointer >= numread then begin\r
71     numread := sourcestream.read(buf,bufsize);\r
72     bufpointer := 0;\r
73     if numread = 0 then fileeof := true;\r
74       \r
75   end;\r
76 end;\r
77 \r
78 function treadtxt.readline;\r
79 var\r
80   a,b,c,d:integer;\r
81 begin\r
82 \r
83   result := '';\r
84   repeat\r
85     checkandread;\r
86     b := numread-1;\r
87 \r
88     {core search loop begin}\r
89     d := -1;\r
90     for a := bufpointer to b do begin\r
91       c := buf[a];\r
92       if (c = 10) or (c = 13) then begin\r
93          d := a;\r
94          break;\r
95       end;\r
96     end;\r
97     {core search loop end}\r
98     \r
99     c := length(result);\r
100     if (d = -1) then begin\r
101       {ran out of buffer before end of line}\r
102       b := numread-bufpointer;\r
103       setlength(result,c+b);\r
104       move(buf[bufpointer],result[c+1],b);\r
105       bufpointer := numread;\r
106       if fileeof then begin\r
107         {we reached the end of the file, return what we have}\r
108         reachedeof := true;\r
109         exit;\r
110       end;\r
111     end else begin\r
112 \r
113       preveol := currenteol;\r
114       currenteol := buf[d];\r
115 \r
116       {end of line before end of buffer}\r
117       if (currenteol = 10) and (preveol = 13) then begin\r
118         {it's the second EOL char of a DOS line ending, don't cause a line}\r
119         bufpointer := d+1;\r
120         eoltype := eoltype_crlf;\r
121       end else begin\r
122         if eoltype = eoltype_none then begin\r
123           if (currenteol = 10) then eoltype := eoltype_lf else eoltype := eoltype_cr;\r
124         end;  \r
125         b := d-bufpointer;\r
126         setlength(result,c+b);\r
127         move(buf[bufpointer],result[c+1],b);\r
128         bufpointer := d+1;\r
129 \r
130         {EOF check}\r
131         if fileeof then begin\r
132           if (bufpointer >= numread) then reachedeof := true;\r
133           if (currenteol = 13) and (bufpointer = numread-1) then if (buf[bufpointer] = 10) then reachedeof := true;\r
134         end;  \r
135 \r
136         exit;\r
137       end;\r
138     end;\r
139   until false;\r
140 end;\r
141 \r
142 function treadtxt.eof:boolean;\r
143 begin\r
144   checkandread;\r
145   result := ((bufpointer >= numread) and fileeof) or reachedeof;\r
146 end;\r
147 \r
148 destructor treadtxt.destroy;\r
149 begin\r
150   if destroysourcestream then if assigned(sourcestream) then sourcestream.destroy;\r
151   inherited destroy;\r
152 end;\r
153 \r
154 end.\r