{
FileIO содержит классы для файлового ввода-вывода.
Copyright © 2016, 2019, 2022–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit FileIO;
{$MODE DELPHI}
interface
uses
Windows,
Lang,
IOStreams;
{%region public }
const
HANDLE_STREAM_GUID = '{74C86B60-AC5B-4E8D-8ACD-29A50B5C1507}';
type
HandleStream = interface;
FileNotOpenException = class;
FileInputStream = class;
FileOutputStream = class;
FileStream = class;
HandleStream = interface(_Interface) [HANDLE_STREAM_GUID]
function isInvalidHandle(): boolean;
function getHandle(): int;
end;
FileNotOpenException = class(IOException)
public
constructor create(const fileName: AnsiString); overload;
constructor create(const fileName: UnicodeString); overload;
end;
FileInputStream = class(InputStream, HandleStream)
strict private
owned: boolean;
handle: int;
public
constructor create(handle: int); overload;
constructor create(const fileName: AnsiString); overload;
constructor create(const fileName: UnicodeString); overload;
destructor destroy; override;
function seekSupported(): boolean; override;
function size(): long; override;
function position(): long; override;
function seek(delta: long): long; override;
function read(const dst: byte_Array1d; offset, length: int): int; overload; override;
function read(): int; overload; override;
function isInvalidHandle(): boolean;
function getHandle(): int;
end;
FileOutputStream = class(OutputStream, HandleStream)
strict private
owned: boolean;
handle: int;
public
constructor create(handle: int); overload;
constructor create(const fileName: AnsiString; appending: boolean = false); overload;
constructor create(const fileName: UnicodeString; appending: boolean = false); overload;
destructor destroy; override;
function write(const src: byte_Array1d; offset, length: int): int; overload; override;
function write(value: int): boolean; overload; override;
function isInvalidHandle(): boolean;
function getHandle(): int;
end;
FileStream = class(InputOutputStream, HandleStream)
strict private
handle: int;
input: FileInputStream;
output: FileOutputStream;
function getInputStream(): FileInputStream;
function getOutputStream(): FileOutputStream;
public
constructor create(const fileName: AnsiString); overload;
constructor create(const fileName: UnicodeString); overload;
destructor destroy; override;
function seekSupported(): boolean; override;
function size(): long; override;
function position(): long; override;
function seek(delta: long): long; override;
function read(const dst: byte_Array1d; offset, length: int): int; overload; override;
function read(): int; overload; override;
function write(const src: byte_Array1d; offset, length: int): int; overload; override;
function write(value: int): boolean; overload; override;
function truncate(): long; override;
function isInvalidHandle(): boolean;
function getHandle(): int;
end;
{%endregion}
{%region routine }
function fileExists(const fileName: AnsiString): boolean; overload;
function fileExists(const fileName: UnicodeString): boolean; overload;
function enumerateFiles(const dirName: AnsiString): UnicodeString_Array1d; overload;
function enumerateFiles(const dirName: UnicodeString): UnicodeString_Array1d; overload;
function enumerateFiles(const dirName, extension: AnsiString): UnicodeString_Array1d; overload;
function enumerateFiles(const dirName, extension: UnicodeString): UnicodeString_Array1d; overload;
function enumerateDirectories(const dirName: AnsiString): UnicodeString_Array1d; overload;
function enumerateDirectories(const dirName: UnicodeString): UnicodeString_Array1d; overload;
function createDirectory(const dirName: AnsiString): boolean; overload;
function createDirectory(const dirName: UnicodeString): boolean; overload;
function destroyDirectory(const dirName: AnsiString; selfDestroying: boolean = true): boolean; overload;
function destroyDirectory(const dirName: UnicodeString; selfDestroying: boolean = true): boolean; overload;
function deleteFile(const fileName: AnsiString): boolean; overload;
function deleteFile(const fileName: UnicodeString): boolean; overload;
function deleteDirectory(const dirName: AnsiString): boolean; overload;
function deleteDirectory(const dirName: UnicodeString): boolean; overload;
function move(const sourceName, destinationName: AnsiString): boolean; overload;
function move(const sourceName, destinationName: UnicodeString): boolean; overload;
function copyFiles(const sourceDirectory, destinationDirectory: AnsiString): boolean; overload;
function copyFiles(const sourceDirectory, destinationDirectory: UnicodeString): boolean; overload;
{%endregion}
implementation
{%region routine }
function fileExists(const fileName: AnsiString): boolean;
begin
result := fileExists(toUTF16String(fileName));
end;
function fileExists(const fileName: UnicodeString): boolean;
var
srec: Windows.WIN32_FIND_DATAW;
shan: int;
begin
initialize(srec);
shan := int(Windows.findFirstFileW(PWideChar(fileName), srec));
if shan <> -1 then begin
result := true;
Windows.findClose(shan);
end else begin
result := false;
end;
end;
function enumerateFiles(const dirName: AnsiString): UnicodeString_Array1d;
begin
result := enumerateFiles(toUTF16String(dirName), '*');
end;
function enumerateFiles(const dirName: UnicodeString): UnicodeString_Array1d;
begin
result := enumerateFiles(dirName, '*');
end;
function enumerateFiles(const dirName, extension: AnsiString): UnicodeString_Array1d;
begin
result := enumerateFiles(toUTF16String(dirName), toUTF16String(extension));
end;
function enumerateFiles(const dirName, extension: UnicodeString): UnicodeString_Array1d;
const
faDirectory = $10;
var
mask: UnicodeString;
dir: UnicodeString;
len: int;
function getDir(): UnicodeString;
var
i: int;
begin
result := '';
for i := length(mask) downto 1 do begin
if mask[i] = directorySeparator then begin
result := copy(mask, 1, i);
break;
end;
end;
end;
procedure addFile(const name: UnicodeString);
var
buf: UnicodeString_Array1d;
begin
if length(result) = len then begin
buf := UnicodeString_Array1d_create(len + 16);
arraycopy(result, 0, buf, 0, len);
result := buf;
end;
result[len] := dir + name;
inc(len);
end;
var
buf: UnicodeString_Array1d;
srec: Windows.WIN32_FIND_DATAW;
shan: int;
begin
result := nil;
mask := dirName + '*.' + extension;
len := 0;
initialize(srec);
shan := int(Windows.findFirstFileW(PWideChar(mask), srec));
dir := getDir();
if shan <> -1 then begin
repeat
if (srec.dwFileAttributes and faDirectory) = 0 then begin
addFile(UnicodeString(srec.cFileName));
end;
until not Windows.findNextFileW(shan, srec);
Windows.findClose(shan);
end;
if len <> length(result) then begin
buf := UnicodeString_Array1d_create(len);
arraycopy(result, 0, buf, 0, len);
result := buf;
end;
end;
function enumerateDirectories(const dirName: AnsiString): UnicodeString_Array1d;
begin
result := enumerateDirectories(toUTF16String(dirName));
end;
function enumerateDirectories(const dirName: UnicodeString): UnicodeString_Array1d;
const
faDirectory = $10;
var
len: int;
procedure addFile(const name: UnicodeString);
var
buf: UnicodeString_Array1d;
begin
if (name <> '.') and (name <> '..') then begin
if length(result) = len then begin
buf := UnicodeString_Array1d_create(len + 16);
arraycopy(result, 0, buf, 0, len);
result := buf;
end;
result[len] := dirName + name;
inc(len);
end;
end;
var
buf: UnicodeString_Array1d;
srec: Windows.WIN32_FIND_DATAW;
shan: int;
begin
result := nil;
len := 0;
initialize(srec);
shan := int(Windows.findFirstFileW(PWideChar(dirName + '*.*'), srec));
if shan <> -1 then begin
repeat
if (srec.dwFileAttributes and faDirectory) <> 0 then begin
addFile(UnicodeString(srec.cFileName));
end;
until not Windows.findNextFileW(shan, srec);
Windows.findClose(shan);
end;
if len <> length(result) then begin
buf := UnicodeString_Array1d_create(len);
arraycopy(result, 0, buf, 0, len);
result := buf;
end;
end;
function createDirectory(const dirName: AnsiString): boolean;
begin
result := createDirectory(toUTF16String(dirName));
end;
function createDirectory(const dirName: UnicodeString): boolean;
var
i: int;
l: int;
procedure nextSeparator();
begin
if i <= l then begin
repeat
inc(i);
until (i > l) or (dirName[i] = directorySeparator);
end;
end;
begin
l := length(dirName);
i := 0;
nextSeparator();
nextSeparator();
while fileExists(System.copy(dirName, 1, i - 1)) do begin
if i > l then begin
result := true;
exit;
end;
nextSeparator();
end;
repeat
result := Windows.createDirectoryW(PWideChar(System.copy(dirName, 1, i - 1)), nil);
if not result then begin
exit;
end;
if i > l then begin
break;
end;
nextSeparator();
until false;
end;
function destroyDirectory(const dirName: AnsiString; selfDestroying: boolean): boolean;
begin
result := destroyDirectory(toUTF16String(dirName), selfDestroying);
end;
function destroyDirectory(const dirName: UnicodeString; selfDestroying: boolean): boolean;
const
faDirectory = $10;
var
srec: Windows.WIN32_FIND_DATAW;
shan: int;
currentFile: UnicodeString;
begin
initialize(srec);
if length(dirName) <= 2 then begin
selfDestroying := false;
end;
shan := int(Windows.findFirstFileW(PWideChar(dirName + directorySeparator + '*.*'), srec));
if shan <> -1 then begin
result := true;
repeat
currentFile := UnicodeString(srec.cFileName);
if (currentFile = '.') or (currentFile = '..') then begin
continue;
end;
currentFile := dirName + directorySeparator + currentFile;
if (srec.dwFileAttributes and faDirectory) <> 0 then begin
result := (int(Windows.setFileAttributesW(PWideChar(currentFile), faDirectory)) <> 0) and destroyDirectory(currentFile, true);
end else begin
result := (int(Windows.setFileAttributesW(PWideChar(currentFile), 0)) <> 0) and (int(Windows.deleteFileW(PWideChar(currentFile))) <> 0);
end;
if not result then begin
break;
end;
until not Windows.findNextFileW(shan, srec);
Windows.findClose(shan);
if result and selfDestroying then begin
result := (int(Windows.setFileAttributesW(PWideChar(dirName), faDirectory)) <> 0) and (int(Windows.removeDirectoryW(PWideChar(dirName))) <> 0);
end;
end else begin
result := false;
end;
end;
function deleteFile(const fileName: AnsiString): boolean;
begin
result := deleteFile(toUTF16String(fileName));
end;
function deleteFile(const fileName: UnicodeString): boolean;
begin
result := int(Windows.deleteFileW(PWideChar(fileName))) <> 0;
end;
function deleteDirectory(const dirName: AnsiString): boolean;
begin
result := deleteDirectory(toUTF16String(dirName));
end;
function deleteDirectory(const dirName: UnicodeString): boolean;
const
faDirectory = $10;
begin
result := (int(Windows.setFileAttributesW(PWideChar(dirName), faDirectory)) <> 0) and (int(Windows.removeDirectoryW(PWideChar(dirName))) <> 0);
end;
function move(const sourceName, destinationName: AnsiString): boolean;
begin
result := move(toUTF16String(sourceName), toUTF16String(destinationName));
end;
function move(const sourceName, destinationName: UnicodeString): boolean;
begin
result := int(Windows.moveFileW(PWideChar(sourceName), PWideChar(destinationName))) <> 0;
end;
function copyFiles(const sourceDirectory, destinationDirectory: AnsiString): boolean;
begin
result := copyFiles(toUTF16String(sourceDirectory), toUTF16String(destinationDirectory));
end;
function copyFiles(const sourceDirectory, destinationDirectory: UnicodeString): boolean;
var
i: int;
j: int;
fragment: long;
fragments: long;
remainder: long;
size: long;
stringArray: UnicodeString_Array1d;
buffer: byte_Array1d;
fileName: UnicodeString;
s: UnicodeString;
inFile: Input;
outFile: Output;
begin
stringArray := enumerateFiles(sourceDirectory);
if not createDirectory(copy(destinationDirectory, 1, length(destinationDirectory) - 1)) then begin
result := false;
exit;
end;
buffer := byte_Array1d_create($0400);
for i := length(stringArray) - 1 downto 0 do begin
fileName := '';
s := stringArray[i];
for j := length(s) downto 1 do begin
if s[j] = DIRECTORY_SEPARATOR then begin
fileName := destinationDirectory + copy(s, j + 1, length(s) - j);
break;
end;
end;
inFile := FileInputStream.create(s);
outFile := FileOutputStream.create(fileName);
try
size := inFile.size();
fragments := divLong(size, length(buffer), remainder);
fragment := 0;
while fragment < fragments do begin
inFile.read(buffer);
outFile.write(buffer);
inc(fragment);
end;
if int(remainder) > 0 then begin
inFile.read(buffer, 0, int(remainder));
outFile.write(buffer, 0, int(remainder));
end;
finally
inFile := nil;
outFile := nil;
end;
end;
buffer := nil;
stringArray := enumerateDirectories(sourceDirectory);
result := length(stringArray) > 0;
for i := length(stringArray) - 1 downto 0 do begin
fileName := '';
s := stringArray[i];
for j := length(s) downto 1 do begin
if s[j] = DIRECTORY_SEPARATOR then begin
fileName := destinationDirectory + copy(s, j + 1, length(s) - j);
break;
end;
end;
copyFiles(s + DIRECTORY_SEPARATOR, fileName + DIRECTORY_SEPARATOR);
end;
end;
{%endregion}
{%region FileNotOpenException }
constructor FileNotOpenException.create(const fileName: AnsiString);
begin
inherited create('Не удалось открыть файл: ' + fileName);
end;
constructor FileNotOpenException.create(const fileName: UnicodeString);
begin
inherited create('Не удалось открыть файл: ' + toUTF8String(fileName));
end;
{%endregion}
{%region FileInputStream }
constructor FileInputStream.create(handle: int);
begin
inherited create();
self.owned := false;
self.handle := handle;
end;
constructor FileInputStream.create(const fileName: AnsiString);
begin
create(toUTF16String(fileName));
end;
constructor FileInputStream.create(const fileName: UnicodeString);
var
attr: int;
begin
inherited create();
if fileExists(fileName) then begin
attr := int(Windows.getFileAttributesW(PWideChar(fileName)));
end else begin
attr := Windows.FILE_ATTRIBUTE_NORMAL;
end;
self.owned := true;
self.handle := int(Windows.createFileW(PWideChar(fileName), Windows.GENERIC_READ, Windows.FILE_SHARE_READ, nil, Windows.OPEN_EXISTING, attr, 0));
end;
destructor FileInputStream.destroy;
begin
if owned and (handle <> int(Windows.INVALID_HANDLE_VALUE)) then begin
Windows.closeHandle(handle);
end;
inherited destroy;
end;
function FileInputStream.seekSupported(): boolean;
begin
result := handle <> int(Windows.INVALID_HANDLE_VALUE);
end;
function FileInputStream.size(): long;
var
r: LongRecord absolute result;
begin
result := 0;
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
r.lo := int(Windows.getFileSize(handle, @(r.hi)));
end;
end;
function FileInputStream.position(): long;
var
r: LongRecord absolute result;
begin
result := 0;
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
r.lo := int(Windows.setFilePointer(handle, 0, @(r.hi), 1));
end;
end;
function FileInputStream.seek(delta: long): long;
var
bufsize: long;
position: long;
r: LongRecord absolute result;
begin
result := 0;
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
bufsize := self.size();
position := self.position();
if delta > bufsize - position then begin
delta := bufsize - position;
end;
if delta < -position then begin
delta := -position;
end;
result := delta;
r.lo := int(Windows.setFilePointer(handle, r.lo, @(r.hi), 1));
end;
end;
function FileInputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
lim: int;
len: int;
begin
lim := offset + length;
len := System.length(dst);
if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
raise ArrayIndexOutOfBoundsException.create('InputStream.read: индекс элемента массива выходит из диапазона.');
end;
result := 0;
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
Windows.readFile(handle, dst[offset], DWORD(length), DWORD(result), nil);
end;
end;
function FileInputStream.read(): int;
var
readed: int;
begin
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
result := 0;
readed := 0;
Windows.readFile(handle, result, 1, DWORD(readed), nil);
if readed <> 1 then begin
result := -1;
end;
end else begin
result := -1;
end;
end;
function FileInputStream.isInvalidHandle(): boolean;
begin
result := handle = int(Windows.INVALID_HANDLE_VALUE);
end;
function FileInputStream.getHandle(): int;
begin
result := handle;
end;
{%endregion}
{%region FileOutputStream }
constructor FileOutputStream.create(handle: int);
begin
inherited create();
self.owned := false;
self.handle := handle;
end;
constructor FileOutputStream.create(const fileName: AnsiString; appending: boolean);
begin
create(toUTF16String(fileName), appending);
end;
constructor FileOutputStream.create(const fileName: UnicodeString; appending: boolean);
var
pos: LongRecord;
attr: int;
h: int;
begin
inherited create();
if fileExists(fileName) then begin
attr := int(Windows.getFileAttributesW(PWideChar(fileName)));
end else begin
attr := Windows.FILE_ATTRIBUTE_NORMAL;
end;
if appending then begin
h := int(Windows.createFileW(PWideChar(fileName), Windows.GENERIC_READ or Windows.GENERIC_WRITE, 0, nil, Windows.OPEN_EXISTING, attr, 3));
if h <> int(Windows.INVALID_HANDLE_VALUE) then begin
pos.value := 0;
pos.lo := int(Windows.setFilePointer(h, pos.lo, @(pos.hi), 2));
end;
end else begin
h := int(Windows.createFileW(PWideChar(fileName), Windows.GENERIC_WRITE, 0, nil, Windows.CREATE_ALWAYS, attr, 0));
end;
self.owned := true;
self.handle := h;
end;
destructor FileOutputStream.destroy;
begin
if owned and (handle <> int(Windows.INVALID_HANDLE_VALUE)) then begin
Windows.closeHandle(handle);
end;
inherited destroy;
end;
function FileOutputStream.write(const src: byte_Array1d; offset, length: int): int;
var
lim: int;
len: int;
begin
lim := offset + length;
len := System.length(src);
if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
raise ArrayIndexOutOfBoundsException.create('OutputStream.write: индекс элемента массива выходит из диапазона.');
end;
result := 0;
if handle <> -1 then begin
Windows.writeFile(handle, src[offset], DWORD(length), DWORD(result), nil);
end;
end;
function FileOutputStream.write(value: int): boolean;
var
writed: int;
begin
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
result := true;
writed := 0;
Windows.writeFile(handle, value, 1, DWORD(writed), nil);
if writed <> 1 then begin
result := false;
end;
end else begin
result := false;
end;
end;
function FileOutputStream.isInvalidHandle(): boolean;
begin
result := handle = int(Windows.INVALID_HANDLE_VALUE);
end;
function FileOutputStream.getHandle(): int;
begin
result := handle;
end;
{%endregion}
{%region FileStream }
constructor FileStream.create(const fileName: AnsiString);
begin
create(toUTF16String(fileName));
end;
constructor FileStream.create(const fileName: UnicodeString);
var
attr: int;
begin
inherited create();
if fileExists(fileName) then begin
attr := int(Windows.getFileAttributesW(PWideChar(fileName)));
self.handle := int(Windows.createFileW(PWideChar(fileName), Windows.GENERIC_READ or Windows.GENERIC_WRITE, 0, nil, Windows.OPEN_EXISTING, attr, 0));
end else begin
attr := Windows.FILE_ATTRIBUTE_NORMAL;
self.handle := int(Windows.createFileW(PWideChar(fileName), Windows.GENERIC_READ or Windows.GENERIC_WRITE, 0, nil, Windows.CREATE_ALWAYS, attr, 0));
end;
end;
destructor FileStream.destroy;
begin
input.free();
output.free();
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
Windows.closeHandle(handle);
end;
inherited destroy;
end;
function FileStream.getInputStream(): FileInputStream;
begin
result := input;
if result = nil then begin
result := FileInputStream.create(handle);
input := result;
end;
end;
function FileStream.getOutputStream(): FileOutputStream;
begin
result := output;
if result = nil then begin
result := FileOutputStream.create(handle);
output := result;
end;
end;
function FileStream.seekSupported(): boolean;
begin
result := getInputStream().seekSupported();
end;
function FileStream.size(): long;
begin
result := getInputStream().size();
end;
function FileStream.position(): long;
begin
result := getInputStream().position();
end;
function FileStream.seek(delta: long): long;
begin
result := getInputStream().seek(delta);
end;
function FileStream.read(const dst: byte_Array1d; offset, length: int): int;
begin
result := getInputStream().read(dst, offset, length);
end;
function FileStream.read(): int;
begin
result := getInputStream().read();
end;
function FileStream.write(const src: byte_Array1d; offset, length: int): int;
begin
result := getOutputStream().write(src, offset, length);
end;
function FileStream.write(value: int): boolean;
begin
result := getOutputStream().write(value);
end;
function FileStream.truncate(): long;
begin
if handle <> int(Windows.INVALID_HANDLE_VALUE) then begin
Windows.setEndOfFile(handle);
result := size();
end else begin
result := 0;
end;
end;
function FileStream.isInvalidHandle(): boolean;
begin
result := handle = int(Windows.INVALID_HANDLE_VALUE);
end;
function FileStream.getHandle(): int;
begin
result := handle;
end;
{%endregion}
end.