3
3
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
4
* obtain one at https://mozilla.org/MPL/2.0/
5
5
*
6
- * Copyright (C) 2009-2021 , Peter Johnson (gravatar.com/delphidabbler).
6
+ * Copyright (C) 2009-2024 , Peter Johnson (gravatar.com/delphidabbler).
7
7
*
8
- * Provides a container for assisting with common file operations.
8
+ * Record with methods that groups common file operations.
9
+ *
10
+ * Copied from main CodeSnip Delphi XE source code UIOUtils units with
11
+ * IsEqualBytes overloaded methods were copied from routines in the UUtils unit.
12
+ * Modified to compile with Delphi 12 and later.
9
13
}
10
14
11
15
12
- unit UIOUtils ;
16
+ unit CSLE.Utils.FileIO ;
13
17
14
18
15
19
interface
16
20
17
21
18
22
uses
19
- // Delphi
20
- SysUtils, Classes, Types;
23
+ System.SysUtils,
24
+ System.Classes,
25
+ System.Types,
26
+ CSLE.Exceptions;
21
27
22
28
type
23
- // / <summary>
24
- // / Container for methods that assist with common file operations.
29
+ // / <summary>Container for methods that assist with common file operations.
25
30
// / </summary>
26
- // / <remarks>
27
- // / TFileIO is used instead of IOUtils.TFile because the assumptions TFile
28
- // / makes about the use of byte order marks with encoded text files are not
29
- // / compatible with the needs of this program.
30
- // / </remarks>
31
+ // / <remarks>TFileIO is used instead of IOUtils.TFile because the assumptions
32
+ // / TFile makes about the use of byte order marks with encoded text files are
33
+ // / not compatible with the needs of this program.</remarks>
31
34
TFileIO = record
32
35
strict private
33
- // / <summary>
34
- // / Appends whole contents of a byte array to a stream.
35
- // / </summary>
36
+
37
+ // / <summary>Test a given number of bytes from the start of two byte arrays
38
+ // / for equality.</summary>
39
+ // / <param name="BA1">[in] First byte array to be compared.</param>
40
+ // / <param name="BA2">[in] Second byte array to be compared.</param>
41
+ // / <param name="Count">[in] Number of bytes to be compared. Must
42
+ // / be greater than zero.</param>
43
+ // / <returns>True if the required number of bytes in the arrays are equal
44
+ // / and both arrays have at least Count bytes. Otherwise False is returned.
45
+ // / </returns>
46
+ // / <remarks>If either BA1 or BA1 have less than Count bytes then False is
47
+ // / returned, regardless of whether the arrays are equal.</remarks>
48
+ class function IsEqualBytes (const BA1, BA2: array of Byte;
49
+ const Count: Integer): Boolean; overload; static;
50
+
51
+ // / <summary>Checks if two byte arrays are equal.</summary>
52
+ // / <param name="BA1">[in] First byte array to be compared.</param>
53
+ // / <param name="BA2">[in] Second byte array to be compared.</param>
54
+ // / <returns>True if the two arrays are equal, False if not.</returns>
55
+ // / <remarks>If both arrays are empty they are considered equal.</remarks>
56
+ class function IsEqualBytes (const BA1, BA2: array of Byte): Boolean;
57
+ overload; static;
58
+
59
+ // / <summary>Appends whole contents of a byte array to a stream.</summary>
36
60
class procedure BytesToStream (const Bytes: TBytes; const Stream: TStream);
37
61
static;
38
- // / <summary>
39
- // / Copies content of a whole stream into a byte array.
40
- // / </summary>
62
+
63
+ // / <summary>Copies content of a whole stream into a byte array.</summary>
41
64
class function StreamToBytes (const Stream: TStream): TBytes; static;
65
+
42
66
public
67
+
43
68
// / <summary>Checks if given byte array begins with the BOM of the given
44
69
// / encoding.</summary>
45
70
// / <remarks>
@@ -50,6 +75,7 @@ TFileIO = record
50
75
// / </remarks>
51
76
class function CheckBOM (const Bytes: TBytes; const Encoding: TEncoding):
52
77
Boolean; overload; static;
78
+
53
79
// / <summary>Checks if given stream begins with the BOM of the given
54
80
// / encoding.</summary>
55
81
// / <remarks>
@@ -62,6 +88,7 @@ TFileIO = record
62
88
// / </remarks>
63
89
class function CheckBOM (const Stream: TStream; const Encoding: TEncoding):
64
90
Boolean; overload; static;
91
+
65
92
// / <summary>Checks if given file begins with the BOM of the given
66
93
// / encoding.</summary>
67
94
// / <remarks>
@@ -72,18 +99,15 @@ TFileIO = record
72
99
// / </remarks>
73
100
class function CheckBOM (const FileName: TFileName;
74
101
const Encoding: TEncoding): Boolean; overload; static;
75
- // / <summary>
76
- // / Writes all the bytes from a byte array to a file.
77
- // / </summary>
102
+
103
+ // / <summary>Writes all the bytes from a byte array to a file.</summary>
78
104
// / <param name="FileName">string [in] Name of file.</param>
79
105
// / <param name="Bytes">TBytes [in] Array of bytes to be written to file.
80
106
// / </param>
81
107
class procedure WriteAllBytes (const FileName: string; const Bytes: TBytes);
82
108
static;
83
109
84
- // / <summary>
85
- // / Writes text to a file.
86
- // / </summary>
110
+ // / <summary>Writes text to a file.</summary>
87
111
// / <param name="FileName">string [in] Name of file.</param>
88
112
// / <param name="Content">string [in] Text to be written to file.</param>
89
113
// / <param name="Encoding">TEncoding [in] Encoding to be used for text in
@@ -94,9 +118,8 @@ TFileIO = record
94
118
class procedure WriteAllText (const FileName, Content: string;
95
119
const Encoding: TEncoding; const UseBOM: Boolean = False); static;
96
120
97
- // / <summary>
98
- // / Writes lines of text to a text file with lines separated by CRLF.
99
- // / </summary>
121
+ // / <summary>Writes lines of text to a text file with lines separated by
122
+ // / CRLF.</summary>
100
123
// / <param name="FileName">string [in] Name of file.</param>
101
124
// / <param name="Lines">array of string [in] Array of lines of text to be
102
125
// / written.</param>
@@ -109,16 +132,12 @@ TFileIO = record
109
132
const Lines: array of string; const Encoding: TEncoding;
110
133
const UseBOM: Boolean = False); static;
111
134
112
- // / <summary>
113
- // / Reads all bytes from a file into a byte array.
114
- // / </summary>
135
+ // / <summary>Reads all bytes from a file into a byte array.</summary>
115
136
// / <param name="FileName">string [in] Name of file.</param>
116
137
// / <returns>TBytes array containing the file's contents.</returns>
117
138
class function ReadAllBytes (const FileName: string): TBytes; static;
118
139
119
- // / <summary>
120
- // / Reads all the text from a text file.
121
- // / </summary>
140
+ // / <summary>Reads all the text from a text file.</summary>
122
141
// / <param name="FileName">string [in] Name of file.</param>
123
142
// / <param name="Encoding">TEncoding [in] Text encoding used by file.
124
143
// / </param>
@@ -130,9 +149,7 @@ TFileIO = record
130
149
class function ReadAllText (const FileName: string;
131
150
const Encoding: TEncoding; const HasBOM: Boolean = False): string; static;
132
151
133
- // / <summary>
134
- // / Reads all the lines of text from a text file.
135
- // / </summary>
152
+ // / <summary>Reads all the lines of text from a text file.</summary>
136
153
// / <param name="FileName">string [in] Name of file.</param>
137
154
// / <param name="Encoding">TEncoding [in] Text encoding used by file.
138
155
// / </param>
@@ -145,9 +162,7 @@ TFileIO = record
145
162
const Encoding: TEncoding; const HasBOM: Boolean = False):
146
163
TStringDynArray; static;
147
164
148
- // / <summary>
149
- // / Copies content of one file to another.
150
- // / </summary>
165
+ // / <summary>Copies content of one file to another.</summary>
151
166
// / <param name="SrcFileName">string [in] Name of file to be copied.
152
167
// / </param>
153
168
// / <param name="DestFileName">string [in] Name of file to receive
@@ -158,23 +173,17 @@ TFileIO = record
158
173
end ;
159
174
160
175
type
161
- // / <summary>Class of exception raised by UIOUtils code .</summary>
162
- EIOUtils = class (Exception);
176
+ // / <summary>Class of exception raised by TFileIO methods .</summary>
177
+ EFileIO = class (Exception);
163
178
164
179
165
180
implementation
166
181
167
182
168
- uses
169
- // Project
170
- UUtils;
171
-
172
-
173
183
resourcestring
174
184
// Error messages
175
185
sBadBOM = ' Preamble of file %s does not match expected encoding' ;
176
186
177
-
178
187
{ TFileIO }
179
188
180
189
class procedure TFileIO.BytesToStream (const Bytes: TBytes;
@@ -186,29 +195,26 @@ class procedure TFileIO.BytesToStream(const Bytes: TBytes;
186
195
187
196
class function TFileIO.CheckBOM (const Bytes: TBytes; const Encoding: TEncoding):
188
197
Boolean;
189
- var
190
- Preamble: TBytes;
191
198
begin
192
199
Assert(Assigned(Encoding), ' TFileIO.CheckBOM: Encoding is nil' );
193
- Preamble := Encoding.GetPreamble;
200
+ var Preamble := Encoding.GetPreamble;
194
201
if Length(Preamble) = 0 then
195
202
Exit(False);
196
203
Result := IsEqualBytes(Bytes, Preamble, Length(Preamble));
197
204
end ;
198
205
199
206
class function TFileIO.CheckBOM (const Stream: TStream;
200
207
const Encoding: TEncoding): Boolean;
201
- var
202
- Bytes: TBytes;
203
- Preamble: TBytes;
204
- OldPos: Int64;
205
208
begin
206
209
Assert(Assigned(Stream), ' TFileIO.CheckBOM: Stream is nil' );
207
210
Assert(Assigned(Encoding), ' TFileIO.CheckBOM: Encoding is nil' );
208
- Preamble := Encoding.GetPreamble;
211
+ var Preamble := Encoding.GetPreamble;
212
+ if Length(Preamble) = 0 then
213
+ Exit(False);
209
214
if Stream.Size < Length(Preamble) then
210
215
Exit(False);
211
- OldPos := Stream.Position;
216
+ var OldPos: Int64 := Stream.Position;
217
+ var Bytes: TBytes;
212
218
SetLength(Bytes, Length(Preamble));
213
219
Stream.Position := 0 ;
214
220
Stream.ReadBuffer(Pointer(Bytes)^, Length(Preamble));
@@ -218,11 +224,9 @@ class function TFileIO.CheckBOM(const Stream: TStream;
218
224
219
225
class function TFileIO.CheckBOM (const FileName: TFileName;
220
226
const Encoding: TEncoding): Boolean;
221
- var
222
- Stream: TStream;
223
227
begin
224
228
Assert(Assigned(Encoding), ' TFileIO.CheckBOM: Encoding is nil' );
225
- Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
229
+ var Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
226
230
try
227
231
Result := CheckBOM(Stream, Encoding);
228
232
finally
@@ -235,11 +239,31 @@ class procedure TFileIO.CopyFile(const SrcFileName, DestFileName: string);
235
239
TFileIO.WriteAllBytes(DestFileName, TFileIO.ReadAllBytes(SrcFileName));
236
240
end ;
237
241
242
+ class function TFileIO.IsEqualBytes (const BA1, BA2: array of Byte): Boolean;
243
+ begin
244
+ if Length(BA1) <> Length(BA2) then
245
+ Exit(False);
246
+ for var I := 0 to Pred(Length(BA1)) do
247
+ if BA1[I] <> BA2[I] then
248
+ Exit(False);
249
+ Result := True;
250
+ end ;
251
+
252
+ class function TFileIO.IsEqualBytes (const BA1, BA2: array of Byte;
253
+ const Count: Integer): Boolean;
254
+ begin
255
+ Assert(Count > 0 , ' TFileIO.IsEqualBytes: Count must be greater than zero' );
256
+ if (Length(BA1) < Int64(Count)) or (Length(BA2) < Int64(Count)) then
257
+ Exit(False);
258
+ for var I := 0 to Pred(Count) do
259
+ if BA1[I] <> BA2[I] then
260
+ Exit(False);
261
+ Result := True;
262
+ end ;
263
+
238
264
class function TFileIO.ReadAllBytes (const FileName: string): TBytes;
239
- var
240
- FS: TFileStream;
241
265
begin
242
- FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
266
+ var FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
243
267
try
244
268
Result := StreamToBytes(FS);
245
269
finally
@@ -249,16 +273,13 @@ class function TFileIO.ReadAllBytes(const FileName: string): TBytes;
249
273
250
274
class function TFileIO.ReadAllLines (const FileName: string;
251
275
const Encoding: TEncoding; const HasBOM: Boolean): TStringDynArray;
252
- var
253
- Lines: TStrings;
254
- I: Integer;
255
276
begin
256
277
Assert(Assigned(Encoding), ' TFileIO.ReadAllLines: Encoding is nil' );
257
- Lines := TStringList.Create;
278
+ var Lines := TStringList.Create;
258
279
try
259
280
Lines.Text := ReadAllText(FileName, Encoding, HasBOM);
260
281
SetLength(Result, Lines.Count);
261
- for I := 0 to Pred(Lines.Count) do
282
+ for var I := 0 to Pred(Lines.Count) do
262
283
Result[I] := Lines[I];
263
284
finally
264
285
Lines.Free;
@@ -267,20 +288,16 @@ class function TFileIO.ReadAllLines(const FileName: string;
267
288
268
289
class function TFileIO.ReadAllText (const FileName: string;
269
290
const Encoding: TEncoding; const HasBOM: Boolean): string;
270
- var
271
- Content: TBytes;
272
- SizeOfBOM: Integer;
273
291
begin
274
- Assert(Assigned(Encoding), ' TFileIO.ReadAllBytes: Encoding is nil' );
275
- Content := ReadAllBytes(FileName);
292
+ Assert(Assigned(Encoding), ' TFileIO.ReadAllText: Encoding is nil' );
293
+ var Content := ReadAllBytes(FileName);
294
+ var SizeOfBOM: Integer := 0 ;
276
295
if HasBOM then
277
296
begin
278
297
SizeOfBOM := Length(Encoding.GetPreamble);
279
298
if (SizeOfBOM > 0 ) and not CheckBOM(Content, Encoding) then
280
- raise EIOUtils.CreateFmt(sBadBOM, [FileName]);
281
- end
282
- else
283
- SizeOfBOM := 0 ;
299
+ raise EFileIO.CreateFmt(sBadBOM, [FileName]);
300
+ end ;
284
301
Result := Encoding.GetString(Content, SizeOfBOM, Length(Content) - SizeOfBOM);
285
302
end ;
286
303
@@ -294,10 +311,8 @@ class function TFileIO.StreamToBytes(const Stream: TStream): TBytes;
294
311
295
312
class procedure TFileIO.WriteAllBytes (const FileName: string;
296
313
const Bytes: TBytes);
297
- var
298
- FS: TFileStream;
299
314
begin
300
- FS := TFileStream.Create(FileName, fmCreate);
315
+ var FS := TFileStream.Create(FileName, fmCreate);
301
316
try
302
317
BytesToStream(Bytes, FS);
303
318
finally
@@ -308,13 +323,11 @@ class procedure TFileIO.WriteAllBytes(const FileName: string;
308
323
class procedure TFileIO.WriteAllLines (const FileName: string;
309
324
const Lines: array of string; const Encoding: TEncoding;
310
325
const UseBOM: Boolean);
311
- var
312
- Line: string;
313
- SB: TStringBuilder;
314
326
begin
315
- SB := TStringBuilder.Create;
327
+ Assert(Assigned(Encoding), ' TFileIO.WriteAllLines: Encoding is nil' );
328
+ var SB := TStringBuilder.Create;
316
329
try
317
- for Line in Lines do
330
+ for var Line in Lines do
318
331
SB.AppendLine(Line);
319
332
WriteAllText(FileName, SB.ToString, Encoding, UseBOM);
320
333
finally
@@ -324,10 +337,9 @@ class procedure TFileIO.WriteAllLines(const FileName: string;
324
337
325
338
class procedure TFileIO.WriteAllText (const FileName, Content: string;
326
339
const Encoding: TEncoding; const UseBOM: Boolean);
327
- var
328
- FS: TFileStream;
329
340
begin
330
- FS := TFileStream.Create(FileName, fmCreate);
341
+ Assert(Assigned(Encoding), ' TFileIO.WriteAllText: Encoding is nil' );
342
+ var FS := TFileStream.Create(FileName, fmCreate);
331
343
try
332
344
if UseBOM then
333
345
BytesToStream(Encoding.GetPreamble, FS);
0 commit comments