Are there standard functions to perform absolute <--> relative path conversion in Delphi?
For example:
- 'Base' path is
'C:\Projects\Project1\'
- Relative path is
'..\Shared\somefile.pas'
- Absolute path is
'C:\Projects\开发者_高级运维Shared\somefile.pas'
I am looking for something like this:
function AbsToRel(const AbsPath, BasePath: string): string;
// '..\Shared\somefile.pas' =
// AbsToRel('C:\Projects\Shared\somefile.pas', 'C:\Projects\Project1\')
function RelToAbs(const RelPath, BasePath: string): string;
// 'C:\Projects\Shared\somefile.pas' =
// RelToAbs('..\Shared\somefile.pas', 'C:\Projects\Project1\')
To convert to the absolute you have :
ExpandFileName
To have the relative path you have :
ExtractRelativePath
I would use PathRelativePathTo
as the first function and PathCanonicalize
as the second. In the latter case, as argument you pass the string sum of the base path and the relative path.
function PathRelativePathTo(pszPath: PChar; pszFrom: PChar; dwAttrFrom: DWORD;
pszTo: PChar; dwAtrTo: DWORD): LongBool; stdcall; external 'shlwapi.dll' name 'PathRelativePathToW';
function AbsToRel(const AbsPath, BasePath: string): string;
var
Path: array[0..MAX_PATH-1] of char;
begin
PathRelativePathTo(@Path[0], PChar(BasePath), FILE_ATTRIBUTE_DIRECTORY, PChar(AbsPath), 0);
result := Path;
end;
function PathCanonicalize(lpszDst: PChar; lpszSrc: PChar): LongBool; stdcall;
external 'shlwapi.dll' name 'PathCanonicalizeW';
function RelToAbs(const RelPath, BasePath: string): string;
var
Dst: array[0..MAX_PATH-1] of char;
begin
PathCanonicalize(@Dst[0], PChar(IncludeTrailingBackslash(BasePath) + RelPath));
result := Dst;
end;
procedure TForm4.FormCreate(Sender: TObject);
begin
ShowMessage(AbsToRel('C:\Users\Andreas Rejbrand\Desktop\file.txt', 'C:\Users\Andreas Rejbrand\Pictures'));
ShowMessage(RelToAbs('..\Videos\movie.wma', 'C:\Users\Andreas Rejbrand\Desktop'));
end;
Of course, if you use a non-Unicode version of Delphi (that is, <= Delphi 2007), you need to use the Ansi functions (*A
) instead of the Unicode functions (*W
).
For what it's worth, my codebase uses SysUtils.ExtractRelativePath
in one direction and the following home-grown wrapper coming back:
function ExpandFileNameRelBaseDir(const FileName, BaseDir: string): string;
var
Buffer: array [0..MAX_PATH-1] of Char;
begin
if PathIsRelative(PChar(FileName)) then begin
Result := IncludeTrailingBackslash(BaseDir)+FileName;
end else begin
Result := FileName;
end;
if PathCanonicalize(@Buffer[0], PChar(Result)) then begin
Result := Buffer;
end;
end;
You'll need to use the ShLwApi
unit for PathIsRelative
and PathCanonicalize
.
The call to PathIsRelative
means that the routine is robust to absolute paths being specified.
So, SysUtils.ExtractRelativePath
can be your AbsToRel
only the parameters are reversed. And my ExpandFileNameRelBaseDir
will serve as your RelToAbs
.
I just brewed this together:
uses
ShLwApi;
function RelToAbs(const ARelPath, ABasePath: string): string;
begin
SetLength(Result, MAX_PATH);
if PathCombine(@Result[1], PChar(IncludeTrailingPathDelimiter(ABasePath)), PChar(ARelPath)) = nil then
Result := ''
else
SetLength(Result, StrLen(@Result[1]));
end;
Thanks to Andreas and David for calling my attention to the Shell Path Handling Functions.
TPath.Combine(S1, S2);
Should be available since Delphi XE.
I am not too certain if this is still needed after 2+ years, but here is a way to get the Relative to Absolute (As for Absolute to Relative I would suggest philnext's ExtractRelativePath
answer):
Unit: IOUtils
Parent: TPath
function GetFullPath(const BasePath: string): string;
It will return the full, absolute path for a given relative path. If the given path is already absolute, it will just return it as is.
Here is the link at Embarcadero: Get Full Path
And here is a link for Path Manipulation Routines
An alternate solution for RelToAbs
is simply:
ExpandFileName(IncludeTrailingPathDelimiter(BasePath) + RelPath)
Check if your solution will works with Relative Path To Full Path in case when you change current directory. This will works:
function PathRelativeToFull(APath : string) : string;
var
xDir : string;
begin
xDir := GetCurrentDir;
try
SetCurrentDir('C:\Projects\Project1\');
Result := ExpandFileName(APath);
finally
SetCurrentDir(xDir);
end{try..finally};
end;
function PathFullToRelative(APath : string; ABaseDir : string = '') : string;
begin
if ABaseDir = '' then
ABaseDir := 'C:\Projects\Project1\';
Result := ExtractRelativePath(ABaseDir, APath);
end;
Another version of RelToAbs (compatible with all Delphi XE versions).
uses
ShLwApi;
function RelPathToAbsPath(const ARelPath, ABasePath: string): string;
var Buff:array[0..MAX_PATH] of Char;
begin
if PathCombine(Buff, PChar(IncludeTrailingPathDelimiter(ABasePath)), PChar(ARelPath)) = nil then
Result := ''
else Result:=Buff;
end;
Combination of two standard methods Combine/GetFullPath (unit IOUtils) should give the desired result:
CombinedPath := TPath.Combine('Base path', 'Relative path');
FileName := TPath.GetFullPath(CombinedPath);
精彩评论