{$INCLUDE ../cDefines.inc}
unit cFileUtils;
{ }
{ File utility functions v3.02 }
{ }
{ This unit is copyright ?2002-2004 by David J Butler }
{ }
{ This unit is part of Delphi Fundamentals. }
{ Its original file name is cFileUtils.pas }
{ The latest version is available from the Fundamentals home page }
{ http://fundementals.sourceforge.net/ }
{ }
{ I invite you to use this unit, free of charge. }
{ I invite you to distibute this unit, but it must be for free. }
{ I also invite you to contribute to its development, }
{ but do not distribute a modified copy of this file. }
{ }
{ A forum is available on SourceForge for general discussion }
{ http://sourceforge.net/forum/forum.php?forum_id=2117 }
{ }
{ Revision history: }
{ 01/06/2002 3.01 Created cFileUtils from cSysUtils. }
{ 12/12/2002 3.02 Revision. }
{ }
interface
uses
{ Delphi }
{$IFDEF OS_MSWIN}
Windows,
{$ENDIF}
SysUtils;
{ }
{ Path functions }
{ }
const
PathSeperator = {$IFDEF OS_UNIX} '/'{$ENDIF}
{$IFDEF OS_MSWIN}'/'{$ENDIF};
function PathHasDriveLetter(const Path: String): Boolean;
function PathIsDriveLetter(const Path: String): Boolean;
function PathIsDriveRoot(const Path: String): Boolean;
function PathIsRoot(const Path: String): Boolean;
function PathIsUNCPath(const Path: String): Boolean;
function PathIsAbsolute(const Path: String): Boolean;
function PathIsDirectory(const Path: String): Boolean;
function PathInclSuffix(const Path: String;
const PathSep: Char = PathSeperator): String;
function PathExclSuffix(const Path: String;
const PathSep: Char = PathSeperator): String;
procedure PathEnsureSuffix(var Path: String;
const PathSep: Char = PathSeperator);
procedure PathEnsureNoSuffix(var Path: String;
const PathSep: Char = PathSeperator);
function PathCanonical(const Path: String;
const PathSep: Char = PathSeperator): String;
function PathExpand(const Path: String; const BasePath: String = '';
const PathSep: Char = PathSeperator): String;
function PathLeftElement(const Path: String;
const PathSep: Char = PathSeperator): String;
procedure PathSplitLeftElement(const Path: String;
var LeftElement, RightPath: String;
const PathSep: Char = PathSeperator);
procedure DecodeFilePath(const FilePath: String;
var Path, FileName: String;
const PathSep: Char = PathSeperator);
function FileNameValid(const FileName: String): String;
function FilePath(const FileName, Path: String; const BasePath: String = '';
const PathSep: Char = PathSeperator): String;
function DirectoryExpand(const Path: String; const BasePath: String = '';
const PathSep: Char = PathSeperator): String;
function UnixPathToWinPath(const Path: String): String;
function WinPathToUnixPath(const Path: String): String;
{ }
{ File operations }
{ MoveFile first attempts a rename, then a copy and delete. }
{ }
type
EFileError = class(Exception);
function GetFileSize(const FileName: String): Int64;
function GetFileDateTime(const FileName: String): TDateTime;
function GetFirstFileNameMatching(const FileMask: String): String;
function DirEntryGetAttr(const FileName: String): Integer;
function DirEntryIsDirectory(const FileName: String): Boolean;
function FileHasAttr(const FileName: String; const Attr: Word): Boolean;
function FileIsReadOnly(const FileName: String): Boolean;
procedure CopyFile(const FileName, DestName: String);
procedure MoveFile(const FileName, DestName: String);
function DeleteFiles(const FileMask: String): Boolean;
{$IFDEF OS_MSWIN}
{ }
{ Logical Drive functions }
{ }
type
TLogicalDriveType = (
DriveRemovable,
DriveFixed,
DriveRemote,
DriveCDRom,
DriveRamDisk,
DriveTypeUnknown);
function DriveIsValid(const Drive: Char): Boolean;
function DriveGetType(const Path: String): TLogicalDriveType;
function DriveFreeSpace(const Path: String): Int64;
{$ENDIF}
{ }
{ Self-testing code }
{ }
procedure SelfTest;
implementation
uses
{ Fundamentals }
cUtils,
cStrings;
{ }
{ Path functions }
{ }
function PathHasDriveLetter(const Path: String): Boolean;
var P: PChar;
begin
Result := False;
if Length(Path) < 2 then
exit;
P := Pointer(Path);
if not (P^ in ['A'..'Z', 'a'..'z']) then
exit;
Inc(P);
if P^ <> ':' then
exit;
Result := True;
end;
function PathIsDriveLetter(const Path: String): Boolean;
begin
Result := (Length(Path) = 2) and PathHasDriveLetter(Path);
end;
function PathIsDriveRoot(const Path: String): Boolean;
begin
Result := (Length(Path) = 3) and PathHasDriveLetter(Path) and
(Path[3] = '/');
end;
function PathIsRoot(const Path: String): Boolean;
begin
Result := ((Length(Path) = 1) and (Path[1] in csSlash)) or
PathIsDriveRoot(Path);
end;
function PathIsUNCPath(const Path: String): Boolean;
var P: PChar;
begin
Result := False;
if Length(Path) < 2 then
exit;
P := Pointer(Path);
if P^ <> '/' then
exit;
Inc(P);
if P^ <> '/' then
exit;
Result := True;
end;
function PathIsAbsolute(const Path: String): Boolean;
begin
if Path = '' then
Result := False else
if PathHasDriveLetter(Path) then
Result := True else
if PChar(Pointer(Path))^ in ['/', '/'] then
Result := True else
Result := False;
end;
function PathIsDirectory(const Path: String): Boolean;
var L: Integer;
P: PChar;
begin
L := Length(Path);
if L = 0 then
Result := False else
if (L = 2) and PathHasDriveLetter(Path) then
Result := True else
begin
P := Pointer(Path);
Inc(P, L - 1);
Result := P^ in csSlash;
end;
end;
function PathInclSuffix(const Path: String; const PathSep: Char): String;
var L: Integer;
P: PChar;
begin
L := Length(Path);
if L = 0 then
Result := '' else
begin
P := Pointer(Path);
Inc(P, L - 1);
if P^ = PathSep then
Result := Path else
Result := Path + PathSep;
end;
end;
procedure PathEnsureSuffix(var Path: String; const PathSep: Char);
begin
Path := PathInclSuffix(Path, PathSep);
end;
procedure PathEnsureNoSuffix(var Path: String; const PathSep: Char);
begin
Path := PathExclSuffix(Path, PathSep);
end;
function PathExclSuffix(const Path: String; const PathSep: Char): String;
var L: Integer;
P: PChar;
begin
L := Length(Path);
if L = 0 then
Result := '' else
begin
P := Pointer(Path);
Inc(P, L - 1);
if P^ = PathSep then
Result := Copy(Path, 1, L - 1) else
Result := Path;
end;
end;
function PathCanonical(const Path: String; const PathSep: Char): String;
var L, M : Integer;
I, J : Integer;
P : StringArray;
Q : PChar;
begin
Result := Path;
// /./ references
M := Length(Result);
Repeat
L := M;
if L = 0 then
exit;
Result := StrReplace('/./', '/', Result);
Result := StrReplace('/./', '/', Result);
M := Length(Result);
Until L = M;
// ./ prefix
StrEnsureNoPrefix(Result, './');
StrEnsureNoPrefix(Result, './');
// /. suffix
StrEnsureNoSuffix(Result, '/.');
StrEnsureNoSuffix(Result, '/.');
// ..
if Pos('..', Result) > 0 then
begin
P := StrSplitChar(Result, PathSep);
Repeat
J := -1;
For I := Length(P) - 1 downto 0 do
if P[I] = '..' then
begin
J := I;
break;
end;
if J = -1 then
break;
M := -1;
For I := J - 1 downto 0 do
if (P[I] = '') or ((I = 0) and PathHasDriveLetter(P[I])) then
break else
if P[I] <> '..' then
begin
M := I;
break;
end;
if M = -1 then
break;
Remove(P, J, 1);
Remove(P, M, 1);
Until False;
Result := StrJoinChar(P, PathSep);
end;
// /../ prefix
While StrMatchLeft(Result, '/../') do
Delete(Result, 1, 3);
While StrMatchLeft(Result, '/../') do
Delete(Result, 1, 3);
if (Result = '/..') or (Result = '/..') then
Result := '';
L := Length(Result);
if L = 0 then
exit;
// X:/../ prefix
Q := Pointer(Result);
if Q^ in ['A'..'Z', 'a'..'z'] then
begin
if StrMatch(Result, ':/../', 2) then
Delete(Result, 4, 3) else
if (L = 5) and StrMatch(Result, ':/..', 2) then
begin
SetLength(Result, 2);
exit;
end;
L := Length(Result);
end;
// single dot
Q := Pointer(Result);
if L = 1 then
begin
if Q^ = '.' then
Result := '';
exit;
end;
// final dot
Inc(Q, L - 2);
if not (Q^ in ['.', '/', '/', ':']) then
begin
Inc(Q);
if Q^ = '.' then
Delete(Result, L, 1);
end;
end;
function PathExpand(const Path: String; const BasePath: String;
const PathSep: Char): String;
begin
if Path = '' then
Result := BasePath else
if PathIsAbsolute(Path) then
Result := Path else
Result := PathInclSuffix(BasePath, PathSep) + Path;
Result := PathCanonical(Result, PathSep);
end;
function PathLeftElement(const Path: String; const PathSep: Char): String;
var I: Integer;
begin
I := PosChar(PathSep, Path);
if I <= 0 then
Result := Path else
Result := Copy(Path, 1, I - 1);
end;
procedure PathSplitLeftElement(const Path: String;
var LeftElement, RightPath: String; const PathSep: Char);
var I: Integer;
begin
I := PosChar(PathSep, Path);
if I <= 0 then
begin
LeftElement := Path;
RightPath := '';
end else
begin
LeftElement := Copy(Path, 1, I - 1);
RightPath := CopyFrom(Path, I + 1);
end;
end;
procedure DecodeFilePath(const FilePath: String; var Path, FileName: String;
const PathSep: Char);
var I: Integer;
begin
I := PosCharRev(PathSep, FilePath);
if I <= 0 then
begin
Path := '';
FileName := FilePath;
end else
begin
Path := Copy(FilePath, 1, I);
FileName := CopyFrom(FilePath, I + 1);
end;
end;
function FileNameValid(const FileName: String): String;
begin
Result := StrReplaceChar(['/', '/', ':', '>', '<', '*', '?'], '_', FileName);
if Result = '.' then
Result := '' else
if Result = '..' then
Result := '_';
end;
function FilePath(const FileName, Path: String; const BasePath: String;
const PathSep: Char): String;
var P, F: String;
begin
F := FileNameValid(FileName);
if F = '' then
begin
Result := '';
exit;
end;
P := PathExpand(Path, BasePath, PathSep);
if P = '' then
Result := F else
Result := PathInclSuffix(P, PathSep) + F;
End;
function DirectoryExpand(const Path: String; const BasePath: String;
const PathSep: Char): String;
begin
Result := PathExpand(PathInclSuffix(Path, PathSep),
PathInclSuffix(BasePath), PathSep);
end;
function UnixPathToWinPath(const Path: String): String;
begin
Result := StrReplaceChar('/', '/',
StrReplaceChar(['/', ':', '<', '>', '|'], '_', Path));
end;
function WinPathToUnixPath(const Path: String): String;
begin
Result := Path;
if PathHasDriveLetter(Path) then
begin
// X: -> /X
Result[2] := Result[1];
Result[1] := '/';
end else
if StrMatchLeft(Path, '//./') then
// //./ -> /
Delete(Result, 1, 3) else
if PathIsUncPath(Path) then
// // -> /
Delete(Result, 1, 1);
Result := StrReplaceChar('/', '/',
StrReplaceChar(['/', ':', '<', '>', '|'], '_', Result));
end;
{ }
{ File operations }
{ }
function GetFileSize(const FileName: String): Int64;
var SRec : TSearchRec;
begin
if FindFirst(FileName, faAnyFile, SRec) <> 0 then
Result := -1 else
begin
{$IFDEF OS_MSWIN}
{$WARNINGS OFF}
Int64Rec(Result).Lo := SRec.FindData.nFileSizeLow;
Int64Rec(Result).Hi := SRec.FindData.nFileSizeHigh;
{$IFDEF DEBUG}{$WARNINGS ON}{$ENDIF}
{$ELSE}
Result := SRec.Size;
{$ENDIF}
FindClose(SRec);
end;
end;
function GetFileDateTime(const FileName: String): TDateTime;
var Age : LongInt;
begin
Age := FileAge(FileName);
if Age = -1 then
Result := 0
else
Result := FileDateToDateTime(Age);
end;
function GetFirstFileNameMatching(const FileMask: String): String;
var SRec : TSearchRec;
begin
Result := '';
if FindFirst(FileMask, faAnyFile, SRec) = 0 then
try
Repeat
if SRec.Attr and faDirectory = 0 then
begin
Result := ExtractFilePath(FileMask) + SRec.Name;
exit;
end;
Until FindNext(SRec) <> 0;
finally
FindClose(SRec);
end;
end;
function DirEntryGetAttr(const FileName: String): Integer;
var SRec : TSearchRec;
begin
if (FileName = '') or PathIsDriveLetter(FileName) then
Result := -1 else
if PathIsRoot(FileName) then
Result := $0800 or faDirectory else
if FindFirst(PathExclSuffix(FileName, '/'), faAnyFile, SRec) = 0 then
begin
Result := SRec.Attr;
FindClose(SRec);
end
else
Result := -1;
end;
function DirEntryIsDirectory(const FileName: String): Boolean;
var SRec : TSearchRec;
begin
if (FileName = '') or PathIsDriveLetter(FileName) then
Result := False else
if PathIsRoot(FileName) then
Result := True else
if FindFirst(PathExclSuffix(FileName, '/'), faDirectory, SRec) = 0 then
begin
Result := SRec.Attr and faDirectory <> 0;
FindClose(SRec);
end
else
Result := False;
end;
{$IFDEF DELPHI6_UP}{$WARN SYMBOL_PLATFORM OFF}{$ENDIF}
function FileHasAttr(const FileName: String; const Attr: Word): Boolean;
var A : Integer;
begin
A := FileGetAttr(FileName);
Result := (A >= 0) and (A and Attr <> 0);
end;
function FileIsReadOnly(const FileName: String): Boolean;
begin
Result := FileHasAttr(FileName, faReadOnly);
end;
procedure CopyFile(const FileName, DestName: String);
var
CopyBuffer : Pointer;
BytesCopied : Longint;
Source, Dest : Integer;
Destination : TFileName;
const
ChunkSize = 8192;
begin
Destination := ExpandFileName(DestName);
if FileHasAttr(Destination, faDirectory) then // if destination is a directory, append file name
Destination := Destination + '/' + ExtractFileName(FileName);
GetMem(CopyBuffer, ChunkSize);
try
Source := FileOpen(FileName, fmShareDenyWrite);
if Source < 0 then
raise EFileError.CreateFmt('Can not open file %s', [FileName]);
try
Dest := FileCreate(Destination);
if Dest < 0 then
raise EFileError.CreateFmt('Can not create file %s', [Destination]);
try
Repeat
BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize);
if BytesCopied > 0 then
FileWrite(Dest, CopyBuffer^, BytesCopied);
Until BytesCopied < ChunkSize;
finally
FileClose(Dest);
end;
finally
FileClose(Source);
end;
finally
FreeMem(CopyBuffer, ChunkSize);
end;
end;
procedure MoveFile(const FileName, DestName: String);
var Destination : String;
Attr : Integer;
begin
Destination := ExpandFileName(DestName);
if not RenameFile(FileName, Destination) then
begin
Attr := FileGetAttr(FileName);
if (Attr < 0) or (Attr and faReadOnly <> 0) then
raise EFileError.Create(Format('Can not move file %s', [FileName]));
CopyFile(FileName, Destination);
DeleteFile(FileName);
end;
end;
function DeleteFiles(const FileMask: String): Boolean;
var SRec : TSearchRec;
Path : String;
begin
Result := FindFirst(FileMask, faAnyFile, SRec) = 0;
if not Result then
exit;
try
Path := ExtractFilePath(FileMask);
Repeat
if (SRec.Name <> '') and (SRec.Name <> '.') and (SRec.Name <> '..') and
(SRec.Attr and (faVolumeID + faDirectory) = 0) then
begin
Result := DeleteFile(Path + SRec.Name);
if not Result then
break;
end;
Until FindNext(SRec) <> 0;
finally
FindClose(SRec);
end;
end;
{$IFDEF DELPHI6_UP}{$WARN SYMBOL_PLATFORM ON}{$ENDIF}
{$IFDEF OS_MSWIN}
{ }
{ Logical Drive functions }
{ }
function DriveIsValid(const Drive: Char): Boolean;
var D : Char;
begin
D := UpCase(Drive);
Result := D in ['A'..'Z'];
if not Result then
exit;
Result := IsBitSet(GetLogicalDrives, Ord(D) - Ord('A'));
end;
function DriveGetType(const Path: String): TLogicalDriveType;
begin
Case GetDriveType(PChar(Path)) of
DRIVE_REMOVABLE : Result := DriveRemovable;
DRIVE_FIXED : Result := DriveFixed;
DRIVE_REMOTE : Result := DriveRemote;
DRIVE_CDROM : Result := DriveCDRom;
DRIVE_RAMDISK : Result := DriveRamDisk;
else
Result := DriveTypeUnknown;
end;
end;
function DriveFreeSpace(const Path: String): Int64;
var D: Byte;
begin
if PathHasDriveLetter(Path) then
D := Ord(UpCase(PChar(Path)^)) - Ord('A') + 1 else
if PathIsUNCPath(Path) then
begin
Result := -1;
exit;
end else
D := 0;
Result := DiskFree(D);
end;
{$ENDIF}
{ }
{ Self-testing code }
{ }
{$ASSERTIONS ON}
procedure SelfTest;
begin
Assert(PathHasDriveLetter('C:'), 'PathHasDriveLetter');
Assert(PathHasDriveLetter('C:/'), 'PathHasDriveLetter');
Assert(not PathHasDriveLetter('/C/'), 'PathHasDriveLetter');
Assert(not PathHasDriveLetter('::'), 'PathHasDriveLetter');
Assert(PathIsAbsolute('/'), 'PathIsAbsolute');
Assert(PathIsAbsolute('/C'), 'PathIsAbsolute');
Assert(PathIsAbsolute('/C/'), 'PathIsAbsolute');
Assert(PathIsAbsolute('C:/'), 'PathIsAbsolute');
Assert(PathIsAbsolute('C:'), 'PathIsAbsolute');
Assert(PathIsAbsolute('/C/../'), 'PathIsAbsolute');
Assert(not PathIsAbsolute(''), 'PathIsAbsolute');
Assert(not PathIsAbsolute('C'), 'PathIsAbsolute');
Assert(not PathIsAbsolute('C/'), 'PathIsAbsolute');
Assert(not PathIsAbsolute('C/D'), 'PathIsAbsolute');
Assert(not PathIsAbsolute('C/D/'), 'PathIsAbsolute');
Assert(not PathIsAbsolute('../'), 'PathIsAbsolute');
Assert(PathIsDirectory('/'), 'PathIsDirectory');
Assert(PathIsDirectory('/C/'), 'PathIsDirectory');
Assert(PathIsDirectory('C:'), 'PathIsDirectory');
Assert(PathIsDirectory('C:/'), 'PathIsDirectory');
Assert(PathIsDirectory('C:/D/'), 'PathIsDirectory');
Assert(not PathIsDirectory(''), 'PathIsDirectory');
Assert(not PathIsDirectory('D'), 'PathIsDirectory');
Assert(not PathIsDirectory('C/D'), 'PathIsDirectory');
Assert(PathInclSuffix('', '/') = '', 'PathInclSuffix');
Assert(PathInclSuffix('C', '/') = 'C/', 'PathInclSuffix');
Assert(PathInclSuffix('C/', '/') = 'C/', 'PathInclSuffix');
Assert(PathInclSuffix('C/D', '/') = 'C/D/', 'PathInclSuffix');
Assert(PathInclSuffix('C/D/', '/') = 'C/D/', 'PathInclSuffix');
Assert(PathInclSuffix('C:', '/') = 'C:/', 'PathInclSuffix');
Assert(PathInclSuffix('C:/', '/') = 'C:/', 'PathInclSuffix');
Assert(PathExclSuffix('', '/') = '', 'PathExclSuffix');
Assert(PathExclSuffix('C', '/') = 'C', 'PathExclSuffix');
Assert(PathExclSuffix('C/', '/') = 'C', 'PathExclSuffix');
Assert(PathExclSuffix('C/D', '/') = 'C/D', 'PathExclSuffix');
Assert(PathExclSuffix('C/D/', '/') = 'C/D', 'PathExclSuffix');
Assert(PathExclSuffix('C:', '/') = 'C:', 'PathExclSuffix');
Assert(PathExclSuffix('C:/', '/') = 'C:', 'PathExclSuffix');
Assert(PathCanonical('', '/') = '', 'PathCanonical');
Assert(PathCanonical('.', '/') = '', 'PathCanonical');
Assert(PathCanonical('./', '/') = '', 'PathCanonical');
Assert(PathCanonical('../', '/') = '../', 'PathCanonical');
Assert(PathCanonical('/../', '/') = '/', 'PathCanonical');
Assert(PathCanonical('/X/../../', '/') = '/', 'PathCanonical');
Assert(PathCanonical('/..', '/') = '', 'PathCanonical');
Assert(PathCanonical('X', '/') = 'X', 'PathCanonical');
Assert(PathCanonical('/X', '/') = '/X', 'PathCanonical');
Assert(PathCanonical('X.', '/') = 'X', 'PathCanonical');
Assert(PathCanonical('.', '/') = '', 'PathCanonical');
Assert(PathCanonical('/X.', '/') = '/X', 'PathCanonical');
Assert(PathCanonical('/X.Y', '/') = '/X.Y', 'PathCanonical');
Assert(PathCanonical('/X.Y/', '/') = '/X.Y/', 'PathCanonical');
Assert(PathCanonical('/A/X..Y/', '/') = '/A/X..Y/', 'PathCanonical');
Assert(PathCanonical('/A/.Y/', '/') = '/A/.Y/', 'PathCanonical');
Assert(PathCanonical('/A/..Y/', '/') = '/A/..Y/', 'PathCanonical');
Assert(PathCanonical('/A/Y../', '/') = '/A/Y../', 'PathCanonical');
Assert(PathCanonical('/A/Y..', '/') = '/A/Y..', 'PathCanonical');
Assert(PathCanonical('X', '/') = 'X', 'PathCanonical');
Assert(PathCanonical('X/', '/') = 'X/', 'PathCanonical');
Assert(PathCanonical('X/Y/..', '/') = 'X', 'PathCanonical');
Assert(PathCanonical('X/Y/../', '/') = 'X/', 'PathCanonical');
Assert(PathCanonical('/X/Y/..', '/') = '/X', 'PathCanonical');
Assert(PathCanonical('/X/Y/../', '/') = '/X/', 'PathCanonical');
Assert(PathCanonical('/X/Y/../..', '/') = '', 'PathCanonical');
Assert(PathCanonical('/X/Y/../../', '/') = '/', 'PathCanonical');
Assert(PathCanonical('/A/././X/./Y/.././.././', '/') = '/A/', 'PathCanonical');
Assert(PathCanonical('C:', '/') = 'C:', 'PathCanonical');
Assert(PathCanonical('C:/', '/') = 'C:/', 'PathCanonical');
Assert(PathCanonical('C:/A/..', '/') = 'C:', 'PathCanonical');
Assert(PathCanonical('C:/A/../', '/') = 'C:/', 'PathCanonical');
Assert(PathCanonical('C:/../', '/') = 'C:/', 'PathCanonical');
Assert(PathCanonical('C:/..', '/') = 'C:', 'PathCanonical');
Assert(PathCanonical('C:/A/../..', '/') = 'C:', 'PathCanonical');
Assert(PathCanonical('C:/A/../../', '/') = 'C:/', 'PathCanonical');
Assert(PathCanonical('/A/B/../C/D/../', '/') = '/A/C/', 'PathCanonical');
Assert(PathCanonical('/A/B/../C/D/../../', '/') = '/A/', 'PathCanonical');
Assert(PathCanonical('/A/B/../C/D/../../../', '/') = '/', 'PathCanonical');
Assert(PathCanonical('/A/B/../C/D/../../../../', '/') = '/', 'PathCanonical');
Assert(PathExpand('', '', '/') = '', 'PathExpand');
Assert(PathExpand('', '/', '/') = '/', 'PathExpand');
Assert(PathExpand('', '/C', '/') = '/C', 'PathExpand');
Assert(PathExpand('', '/C/', '/') = '/C/', 'PathExpand');
Assert(PathExpand('../', '/C/', '/') = '/', 'PathExpand');
Assert(PathExpand('..', '/C/', '/') = '', 'PathExpand');
Assert(PathExpand('/..', '/C/', '/') = '', 'PathExpand');
Assert(PathExpand('/../', '/C/', '/') = '/', 'PathExpand');
Assert(PathExpand('A', '../', '/') = '../A', 'PathExpand');
Assert(PathExpand('../', '../', '/') = '../../', 'PathExpand');
Assert(PathExpand('/', '', '/') = '/', 'PathExpand');
Assert(PathExpand('/', '/C', '/') = '/', 'PathExpand');
Assert(PathExpand('/A', '/C/', '/') = '/A', 'PathExpand');
Assert(PathExpand('/A/', '/C/', '/') = '/A/', 'PathExpand');
Assert(PathExpand('/A/B', '/C', '/') = '/A/B', 'PathExpand');
Assert(PathExpand('A/B', '/C', '/') = '/C/A/B', 'PathExpand');
Assert(PathExpand('A/B', '/C', '/') = '/C/A/B', 'PathExpand');
Assert(PathExpand('A/B', '/C/', '/') = '/C/A/B', 'PathExpand');
Assert(PathExpand('A/B', '/C/', '/') = '/C/A/B', 'PathExpand');
Assert(PathExpand('A/B', 'C/D', '/') = 'C/D/A/B', 'PathExpand');
Assert(PathExpand('../A/B', 'C/D', '/') = 'C/A/B', 'PathExpand');
Assert(PathExpand('../A/B', '/C/D', '/') = '/C/A/B', 'PathExpand');
Assert(PathExpand('../../A/B', 'C/D', '/') = 'A/B', 'PathExpand');
Assert(PathExpand('../../A/B', '/C/D', '/') = '/A/B', 'PathExpand');
Assert(PathExpand('../../../A/B', '/C/D', '/') = '/A/B', 'PathExpand');
Assert(PathExpand('/../A/B', '/C/D', '/') = '/A/B', 'PathExpand');
Assert(PathExpand('../A/B', '/../C/D', '/') = '/C/A/B', 'PathExpand');
Assert(PathExpand('../A/B', '../C/D', '/') = '../C/A/B', 'PathExpand');
Assert(PathExpand('../A/B', 'C:/C/D', '/') = 'C:/C/A/B', 'PathExpand');
Assert(PathExpand('../A/B/', 'C:/C/D', '/') = 'C:/C/A/B/', 'PathExpand');
Assert(FilePath('C', '../X/Y', 'A/B', '/') = 'A/X/Y/C', 'FilePath');
Assert(FilePath('C', '/X/Y', 'A/B', '/') = '/X/Y/C', 'FilePath');
Assert(FilePath('C', '', 'A/B', '/') = 'A/B/C', 'FilePath');
Assert(FilePath('', '/X/Y', 'A/B', '/') = '', 'FilePath');
Assert(FilePath('C', 'X/Y', 'A/B', '/') = 'A/B/X/Y/C', 'FilePath');
Assert(FilePath('C', 'X/Y', '', '/') = 'X/Y/C', 'FilePath');
Assert(DirectoryExpand('', '', '/') = '', 'DirectoryExpand');
Assert(DirectoryExpand('', '/X', '/') = '/X/', 'DirectoryExpand');
Assert(DirectoryExpand('/', '/X', '/') = '/', 'DirectoryExpand');
Assert(DirectoryExpand('/A', '/X', '/') = '/A/', 'DirectoryExpand');
Assert(DirectoryExpand('/A/', '/X', '/') = '/A/', 'DirectoryExpand');
Assert(DirectoryExpand('/A/B', '/X', '/') = '/A/B/', 'DirectoryExpand');
Assert(DirectoryExpand('A', '/X', '/') = '/X/A/', 'DirectoryExpand');
Assert(DirectoryExpand('A/', '/X', '/') = '/X/A/', 'DirectoryExpand');
Assert(DirectoryExpand('C:', '/X', '/') = 'C:/', 'DirectoryExpand');
Assert(DirectoryExpand('C:/', '/X', '/') = 'C:/', 'DirectoryExpand');
Assert(UnixPathToWinPath('/c/d.f') = '/c/d.f', 'UnixPathToWinPath');
Assert(WinPathToUnixPath('/c/d.f') = '/c/d.f', 'WinPathToUnixPath');
end;
end.
相关推荐
高校技术转移办公室人员如何构建技术交易平台的智能推荐机制?.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。
PDF补丁丁终极指南:如何批量处理PDF文档提升10倍效率
PDF补丁丁是一款专业的PDF工具箱,专为中级用户和技术爱好者设计,提供完整的PDF批量处理解决方案。无论是编辑书签、合并拆分文档、提取页面、优化文件体积,还是解除文档限制,PDF补丁丁都能通过高效的批量操作大幅提升工作效率。本文将深入解析PDF补丁丁的核心功能与高级配置,帮助您掌握专业级的PDF批量处理技巧。 ## 📊 痛点场景:PDF处理中的常见挑战与解决方案 ### 场景一:多文档批量
基于mongodb, express, react, node开发的商店应用
1、角色管理,支持买家和卖家 2、 支持创建、编辑、删除拍卖 3、支持投标
把视频里的硬字幕抠成SRT:VSE离线字幕提取实操
你可能碰到过这种情况:课程视频或纪录片里是烧录在画面上的硬字幕,想把它转成文本或复用字幕,可视频没有外挂字幕轨,在线OCR服务又要上传整个视频,不太放心。VSE(video-subtitle-extractor)干的就是视频字幕提取这件事:本地OCR识别画面字幕区域的文字,直接产出标准SRT文件,所有数据都留在自己机器上。 [
本资源是 CrewAI 风格多 Agent 可运行工程包:研究→写作→审校等流水线样例。无 API Key 时自动降级 Mock 引擎跑通;有 DASHSCOPE_API_KEY 时可接真实 CrewAI + 通义千问。含中文 README、samples、smoke 留档、单元测试。本包不含密钥。关联博客:https://blog.csdn.net/mtl1994
国央企创新负责人如何通过平台实现技术需求的高效挖掘与转化?.docx
国央企创新负责人如何通过平台实现技术需求的高效挖掘与转化?
概率神经网络的分类预测-基于PNN的变压器故障诊断(Matlab代码实现)
内容概要:本文介绍了基于Plecs软件构建的全桥LLC谐振变换器仿真模型,旨在通过高精度仿真手段深入研究LLC谐振变换器的工作原理、动态特性及其性能优化。该模型完整涵盖了主电路拓扑结构、谐振腔参数设计、软开关实现机制以及闭环控制策略,能够精确模拟变换器在不同负载与输入条件下的电压增益、效率表现及开关器件的零电压开通(ZVS)特性。通过对谐振频率、变比及负载变化的仿真分析,可有效验证设计合理性,优化系统效率,并为实际硬件开发提供可靠的理论依据和技术支持。; 适合人群:具备扎实电力电子技术基础,从事高频开关电源、谐振变换器设计、新能源变换系统研发的工程师及高校科研人员。; 使用场景及目标:①用于LLC谐振变换器的教学演示与原理验证,帮助学生理解软开关技术与谐振行为;②支持研究人员开展新型控制算法(如变频控制、峰值增益优化)的仿真验证;③辅助工程师在产品开发前期进行参数扫掠与性能预估,缩短研发周期; 阅读建议:建议结合具体设计指标(如输入电压范围、输出功率)调整仿真参数,重点关注谐振电流与励磁电流的波形关系、ZVS实现条件及效率随负载的变化趋势,并将仿真结果与理论计算进行对比,以全面提升对LLC变换器设计规律的掌握。
通信行业基于分层架构与协议适配的接口测试自动化方案:多协议系统回归验证与CI/CD集成
内容概要:本文围绕通信行业中接口测试自动化实践方案的应用,提出了一套从分层框架到协议级落地的完整方法论。针对通信系统多协议并存、环境复杂的特点,强调需构建支持多协议适配、环境仿真、数据闭环和CI/CD集成的自动化体系。通过分层架构、数据驱动、契约校验、环境仿真等关键技术,提升测试可维护性和覆盖率,并结合Python + pytest + YAML的轻量级代码实例,展示了如何实现业务流程封装、用例数据分离与标准化断言,适用于多种典型通信场景。; 适合人群:通信行业从事软件测试、自动化开发、系统集成的技术人员,尤其是有接口测试经验并希望向多协议、高可靠性测试拓展的工程师。; 使用场景及目标:①核心网SBA服务化接口的自动化回归验证;②基站控制面、通信模块协议栈的功能与健壮性测试;③电力、铁路等行业专用通信协议的一致性验收;④实现与CI/CD流水线集成的质量门禁与持续反馈。; 阅读建议:此资源注重工程实践与架构设计结合,建议读者在掌握基本测试框架基础上,结合实际项目模仿其分层思想与数据驱动模式,逐步扩展至协议解析、日志联动断言和全链路观测能力。
人力资源大模型技术领域人事面试全流程解析:从准备到入职的完整指南设计
内容概要:本文档系统梳理了求职者在应聘过程中所需掌握的人事面试全流程及相关技巧,涵盖面试前准备、面试流程解析、常见问题应对策略、聊天话术、offer处理及试用期管理等内容。重点提供了自我介绍、离职原因、薪资期望、职业规划等高频问题的回答范例,并强调了仪容仪表、沟通礼仪、非语言行为等细节对面试成败的影响。同时,文档还详述了Boss直聘平台的高效使用方法、背景调查应对方式以及入职材料准备事项,帮助求职者全面提升求职成功率。; 适合人群:正在寻找工作的应届毕业生、有1-3年工作经验的技术从业者,尤其是从事大模型、人工智能、大数据等相关领域的研发人员。; 使用场景及目标:①用于指导求职者系统化准备人事面试,提升通过率;②帮助求职者规范自我表达,合理设定薪资与城市预期,安全度过试用期;③提供与HR沟通的标准话术和实战案例,增强求职主动性与专业性。; 阅读建议:建议求职者结合自身实际情况,提前模拟练习文档中的问答范例和聊天话术,重点关注自我介绍、离职原因、职业规划等核心问题的表述逻辑,确保真实、可信、积极。同时,在实际操作中注意信息保密,谨慎对待背景调查环节。
n8n 自托管工作流自动化实战速查包(部署/环境变量/HTTPS反代/工作流/AI Agent/调度/排错/升级)
想把自动化工作流、AI Agent 编排、跨应用集成收回自己服务器,n8n 的官方资料却散落在 docs.n8n.io、GitHub 与源码注释里:Code 节点的内置/外部模块为什么默认禁用、SQLite 换 PostgreSQL 为什么没有一键迁移、反代漏转发 Host 头 Webhook 为什么打不通、设了队列模式却没起 Redis 和 worker 工作流为什么不动、执行历史的清理周期和超时单位怎么配才对、加密密钥 N8N_ENCRYPTION_KEY 丢了凭证为什么全失效——每个点都要翻好几页。本包按「部署 → 配置 → 上 HTTPS → 第一个工作流 → AI Agent → 调度 → 错误重试 → 加固 → 排错 → 升级」一条线整理,含 docker-compose(单机 + PostgreSQL + Caddy 反代)、.env 示例(26 处 YOUR_* 占位)、Caddyfile、宿主机备份脚本 4 份模板,以及零依赖自检脚本(合法 env exit 0、缺项/错误 env exit 1 并逐条打印,包内 17 文件自检 8 组 ALL PASS)。n8n 采用 Sustainable Use License(非标准开源许可),本包不含程序本体,请在官方仓库自行获取;示例一律 YOUR_* 占位,替换成你自己的域名、令牌、路径即可落地。
状态估计【卡尔曼滤波kalman】UKF、EKF、粒子滤波PF学习与研究(Matlab代码实现)
内容概要:本文围绕非线性状态估计中的三种经典滤波算法——无迹卡尔曼滤波(UKF)、扩展卡尔曼滤波(EKF)和粒子滤波(PF)展开系统性学习与研究,重点在于通过Matlab编程实现这些算法的核心流程,并将其应用于实际系统的状态估计问题中。文档不仅详细阐述了各算法的数学原理、适用条件与性能差异,还提供了完整的仿真代码与案例分析,帮助读者深入理解其在非线性、非高斯环境下的表现特性。同时,资源拓展至EKF与神经网络融合训练、多源数据融合等高级应用场景,展现了其在智能控制、无人机导航、电力系统状态估计、目标跟踪等领域的广泛应用价值。; 适合人群:具备一定信号处理、控制理论或状态估计基础的研究生、科研人员及工程技术人员,尤其适用于从事自动化、机器人、导航、电力电子与智能系统等相关方向的研究与开发工作者;; 使用场景及目标:① 掌握UKF、EKF与PF在非线性系统中的建模思路与Matlab实现方法,理解其优缺点与适用边界;② 提升科研仿真能力,能够独立构建状态估计模型并进行算法对比验证;③ 将所学方法应用于如电池SOC估算、无人机姿态估计、传感器融合、故障诊断等实际工程问题中;; 阅读建议:建议结合Matlab动手实践,逐行解读并调试所提供的代码,深入理解算法实现细节;同时可延伸学习文中提及的数据融合、神经网络与滤波器结合等进阶主题,以构建完整的状态估计技术知识体系。
双闭环PWM整流器VOC控制 +SVPWM调制仿真(Simulink仿真实现)
内容概要:本文详细阐述了基于Simulink的双闭环PWM整流器电压定向控制(VOC)与空间矢量脉宽调制(SVPWM)的仿真建模与实现方法,系统讲解了电力电子系统中高性能整流器的核心控制策略。内容涵盖VOC控制的基本原理、双闭环控制系统结构设计(电压外环与电流内环的协同作用)、dq坐标系下的数学建模、PI控制器参数整定、电网电压锁相环(PLL)技术,以及SVPWM调制算法的具体实现过程。通过构建完整的Simulink仿真模型,验证了该控制策略在提升系统动态响应速度、实现单位功率因数运行、稳定直流侧电压及抑制谐波等方面的优越性能,为高精度电能质量控制提供了有效的技术方案。; 适合人群:电气工程、自动化、电力电子与电力传动等相关专业的本科生、研究生、科研人员,以及从事新能源发电、电机驱动、电力系统仿真与工业自动化等领域技术研发的工程师。; 使用场景及目标:①深入理解PWM整流器的工作机理与先进控制理论;②掌握VOC控制与SVPWM调制的技术细节与工程实现方法;③独立搭建并调试双闭环控制系统仿真模型,服务于课程设计、毕业课题或科研项目;④为后续研究电网不平衡、弱电网条件、能量回馈控制等复杂工况下的改进控制策略奠定坚实基础。; 阅读建议:建议结合电力电子技术、自动控制原理和电机与拖动等基础知识,按照文档逻辑逐步完成Simulink模型搭建,重点关注坐标变换、闭环结构设计与控制器参数调节,并通过不同负载与电网条件下的仿真对比,深入分析系统动静态性能表现。
Plecs全桥LLC谐振变换器仿真模型
内容概要:本文档系统性地整合了电力电子与控制系统仿真领域的丰富资源,聚焦于基于Plecs和Simulink平台的高频软开关变换器建模与仿真,涵盖全桥LLC、双向CLLC、DAB、PFC、Vienna整流器、Buck-Boost等多种典型拓扑结构。资源内容不仅包括闭环控制(如变频控制、移相混合控制、双闭环、恒压恒流双竞争控制)的仿真模型,还配套提供详细的参数设计报告、Mathcad计算书、PPT说明文档及参考文献,构建了从理论建模到工程实现的完整技术链条。此外,文档拓展至状态估计(如卡尔曼滤波、EKF、UKF、PF)与智能算法(如BP神经网络、模型预测控制)在电力系统中的融合应用,为先进控制策略的研究与复现提供了坚实支撑。; 适合人群:面向具备电力电子、自动控制或电气工程等相关专业背景,熟练掌握MATLAB/Simulink/Plecs等仿真工具,从事高频开关电源、新能源变换系统、储能系统或车载电源研发的研究生、高校科研人员及企业工程技术人员。; 使用场景及目标:① 学习并掌握全桥LLC等谐振变换器的核心工作原理与闭环控制系统设计方法,特别是变频与移相控制策略的实现细节;② 利用提供的仿真模型与Mathcad计算工具,快速完成实际项目中的参数设计、性能验证与优化迭代;③ 结合状态估计算法与神经网络等智能方法,开展电力电子系统状态观测、故障诊断或控制策略创新等前沿科研课题的复现与探索。; 阅读建议:建议严格按照资源目录结构循序渐进地查阅,优先下载并运行核心仿真模型,结合对应的设计报告与计算书深入理解其建模逻辑与参数推导过程,重点关注闭环控制架构、软启动策略及中点电位平衡等关键技术环节,并可进一步将状态估计与智能算法模块融入现有模型,探索其在系统优化中的应用潜力。
隔离型全桥LLC谐振变化器建模及参数设计资料【仿真+设计报告+计算书】
内容概要:本文档围绕“Plecs全桥LLC谐振变换器仿真模型”展开,提供了一套完整的基于Plecs平台的隔离型全桥LLC谐振变换器建模与参数设计资料,包含仿真模型、设计报告及计算书。该资源系统阐述了LLC谐振变换器的主电路拓扑结构、谐振元件(谐振电感、励磁电感、谐振电容)的参数设计方法、软开关实现机理以及闭环变频控制策略,重点分析了谐振腔参数对电压增益、效率及动态响应的影响,支持在不同输入电压与负载条件下进行稳态与瞬态性能仿真验证。文档还整合了多种相关电力电子拓扑的仿真资源,如CLLC、DAB、PWM整流器等,体现了其在现代高效电源系统设计中的广泛适用性与工程参考价值。; 适合人群:具备扎实电力电子技术基础,从事高频开关电源、新能源并网变换器、电动汽车充电系统、微电网能量转换装置研发的工程师、高校研究生及科研人员。; 使用场景及目标:①用于全桥LLC谐振变换器的教学演示与工作原理验证;②支撑科研项目中对高效率软开关拓扑的参数优化与先进控制算法(如变频控制、软启动策略)的研究;③作为工业级电源产品开发的仿真基准模型,加速原型设计、控制逻辑验证与性能优化进程。; 阅读建议:建议使用者结合Plecs软件官方手册与经典LLC理论教材同步学习,重点关注谐振参数匹配原则与闭环控制环路的设计细节,通过调整模型中的关键参数开展对比仿真,深入掌握LLC变换器的频率特性、软开关实现条件及系统稳定性规律。
【扩展卡尔曼滤波器EKF】基于UWB雷达的SLAM能够在室内环境内映射自然点地标,并改善机器人定位(Matlab代码实现)
内容概要:本文介绍了基于Plecs软件构建的全桥LLC谐振变换器仿真模型,重点围绕LLC谐振变换器的工作原理、建模方法与仿真设计展开。该模型可用于研究全桥LLC在软开关条件下的动态响应特性、效率优化及参数设计,涵盖谐振频率调节、变频控制策略、电压增益特性等关键技术内容,帮助深入理解LLC拓扑在高频高效电源转换中的应用。; 适合人群:具备电力电子基础知识,从事电源设计、新能源变换系统研发的工程师及研究生。; 使用场景及目标:①用于教学与科研中对LLC谐振变换器工作模态的分析;②辅助完成高性能DC-DC变换器的参数设计与控制策略验证;③支持对软开关技术、谐振网络优化等方面的深入研究; 阅读建议:建议结合其他LLC相关设计资料与仿真工具(如Simulink)进行对比学习,重点关注谐振参数选取与仿真波形分析,以提升对实际工程问题的解决能力。
单相boost功率因数校正 PFC电路仿真(Simulink仿真实现)
内容概要:本文详细介绍了基于Plecs软件构建的全桥LLC谐振变换器仿真模型,系统阐述了LLC谐振变换器的工作原理、建模方法及仿真实现过程。该模型能够有效分析变换器在不同工况下的动态响应特性,包括软开关的实现条件、谐振参数的设计影响、系统效率优化等方面,并支持开环与闭环控制策略的验证。文档深入探讨了LLC拓扑的电压增益特性、零电压开关(ZVS)的实现机制以及磁性元件参数对系统性能的作用,配套提供了完整的仿真资源体系,涵盖参数计算、控制策略设计与性能评估等内容,形成了从理论到实践的完整研究闭环,有助于深入掌握LLC谐振变换器的核心设计要素与工程应用要点。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校师生,以及从事开关电源、新能源变换器研发的工程技术人员。; 使用场景及目标:①用于教学与科研中对LLC谐振变换器工作机理的理解与验证;②辅助工程师完成实际产品开发中的参数设计与控制策略优化;③支持对软开关技术、高频高效电源变换等前沿方向的研究。; 阅读建议:建议结合Plecs或Simulink等仿真工具动手实践,对照文档中的模型结构与参数设置进行仿真复现,并通过调整谐振腔参数、开关频率等变量观察系统响应变化,以加深对LLC变换器非线性特性和控制难点的理解。
国央企创新负责人如何利用平台实现创新资源的系统化配置与跨区域协同?.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。
港股十档tick数据提供逐笔更新行情,包含时间戳、最新价、涨跌幅、成交量与成交额,以及买一至买十、卖一至卖十的价格和挂单量 逐笔成交数据记录每笔交易的时间、成交价、成交量及买卖方向 分钟数据按每分钟聚
港股十档tick数据提供逐笔更新行情,包含时间戳、最新价、涨跌幅、成交量与成交额,以及买一至买十、卖一至卖十的价格和挂单量。逐笔成交数据记录每笔交易的时间、成交价、成交量及买卖方向。分钟数据按每分钟聚合,涵盖开盘价、最高价、最低价、收盘价、成交量和成交额。上述数据覆盖十五年历史,以csv表格形式呈现,字段齐全,适合直接用于量化回测或行情分析。 数据源:CMES金融数据库
475



被折叠的 条评论
为什么被折叠?



