Author: Jonas Bilinkevicius
How to count the lines of text contained in a text file
Answer:
Solve 1:
The fastest way would be to count the instances of #13#10 yourself. However you
need to be careful because #13 and #10 could easily be swapped to give #10#13
instead which makes this kind of counting more difficult. In this case it's far
easier just to count the instances of one of them and this has the bonus of being
more compatible with non-Windows (ie. non CR/LF'd) files - not all operating
systems bother with both #13 and #10. The following is a basic implementation of
the code:
1
2 function CountLines(const FileName: string): integer;
3 const
4 BufferSize = 1024;
5 SearchByte = 10;
6 var
7 FileHandle, BytesRead, Index: integer;
8 Buffer: array[1..BufferSize] of byte;
9 begin
10 FileHandle := FileOpen(FileName, fmOpenRead or fmShareDenyWrite);
11 BytesRead := FileRead(FileHandle, Buffer[1], BufferSize);
12 if (BytesRead > 0) then
13 Result := 1
14 else
15 Result := 0;
16 repeat
17 for Index := 1 to Min(BufferSize, BytesRead) do
18 begin
19 if (Buffer[Index] = SearchByte) then
20 Inc(Result);
21 end;
22 BytesRead := FileRead(FileHandle, Buffer[1], BufferSize);
23 until
24 BytesRead <= 0;
25 FileClose(FileHandle);
26 end;
This code is searching for #10's in the file, and treating this as a line
delimeter. It takes care of the case where an empty file has 0 lines but a file
with no #10s has one line in the initialisation of the Result return value. You can
easily modify the seach byte and/or the buffer size.
Solve 2:
If it is a smaller file (< 1 MB) load it into a TStringlist and look at the
stringlists Count property. If it is larger you need to read it completely and
count lines. A simple loop would be this:
27
28 function CountLines(const filename: string): Integer;
29 var
30 buffer: array[0..4095] of Char;
31 f: Textfile;
32 begin
33 Result := 0;
34 Assignfile(f, filename);
35 Reset(f);
36 try
37 SetTextBuffer(f, buffer, sizeof(buffer));
38 while not Eof(f) do
39 begin
40 readLn(f);
41 Inc(result);
42 end;
43 finally
44 Closefile(f);
45 end;
46 end;
Using a larger than the default buffer of 128 bytes speeds the reading somewhat.
Solve 3:
Buffering can help quit a bit:
47
48 function TextLineCount_BufferedStream(const Filename: TFileName): Integer;
49 const
50 MAX_BUFFER = 1024 * 1024;
51 var
52 oStream: TFileStream;
53 sBuffer: string;
54 iBufferSize: Integer;
55 iSeek: Integer;
56 bCarry: Boolean;
57 begin
58 Result := 0;
59 bCarry := False;
60 oStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
61 try
62 SetLength(sBuffer, MAX_BUFFER);
63 repeat
64 iBufferSize := oStream.read(sBuffer[1], MAX_BUFFER);
65 if iBufferSize <= 0 then
66 break;
67 {Skip LFs that follow a CR - even if it falls in seperate buffers}
68 iSeek := 1;
69 if bCarry and (sBuffer[1] = #10) then
70 Inc(iSeek);
71 while iSeek <= iBufferSize do
72 begin
73 case sBuffer[iSeek] of
74 #10:
75 Inc(Result);
76 #13:
77 if iSeek = iBufferSize then
78 Inc(Result)
79 else if sBuffer[iSeek + 1] <> #10 then
80 Inc(Result)
81 else
82 begin
83 Inc(Result);
84 Inc(iSeek);
85 end;
86 end;
87 Inc(iSeek);
88 end;
89 {Set carry flag for next pass}
90 bCarry := (sBuffer[iBufferSize] = #13);
91 until
92 iBufferSize < MAX_BUFFER;
93 finally
94 FreeAndNil(oStream);
95 end;
96 end;
|