Преобразование между абсолютным и относительным путями в Delphi

30

Существуют ли стандартные функции для выполнения абсолютного &lt - - относительного преобразования пути в Delphi?

Например:

  • "Базовый" путь 'C:\Projects\Project1\'
  • Относительный путь '..\Shared\somefile.pas'
  • Абсолютный путь 'C:\Projects\Shared\somefile.pas'

Я ищу что-то вроде этого:

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\')  
  • 1
    такое абсолютный путь «C: \ Projects \ Shared \ somefile.pas» или «C: \ Projects \ Project1 \ Shared \ somefile.pas»?
  • 2
    @philnext Это C:\Projects\Shared\somefile.pas - .. обозначает папку, которая в свою очередь удаляет `Project1` из базового (или корневого) пути.
Теги:
path

9 ответов

31
Лучший ответ

Я использовал бы PathRelativePathTo в качестве первой функции и PathCanonicalize как второй. В последнем случае в качестве аргумента вы передаете строчную сумму базового пути и относительного пути.

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;

Конечно, если вы используете версию Delphi, отличную от Unicode (то есть, <= Delphi 2007), вам нужно использовать функции Ansi (*A) вместо функций Unicode (*W).

  • 2
    Не нужно объявлять PathCanonicalize самостоятельно, просто удалите его из ShLwApi
  • 0
    @ Давид: Да, они оба там, я вижу сейчас.
Показать ещё 1 комментарий
43

Чтобы преобразовать в абсолют, вы имеете:

ExpandFileName

Чтобы иметь относительный путь:

ExtractRelativePath

  • 0
    @ Андреас: Да, но это требует некоторой работы, чтобы соответствовать вопросу.
  • 8
    ExpandFileName здесь не годится. Он использует рабочий каталог, а не указанный пользователем каталог. Вам понадобится что-то вроде ответа Андреаса или мой собственный.
Показать ещё 2 комментария
12

Для того, что стоит, моя кодовая база использует SysUtils.ExtractRelativePath в одном направлении, и следующая домашняя обертка возвращается:

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;

Вам понадобится блок ShLwApi для PathIsRelative и PathCanonicalize.

Вызов PathIsRelative означает, что подпрограмма является надежной для заданных абсолютных путей.

Итак, SysUtils.ExtractRelativePath может быть вашим AbsToRel, только параметры меняются на противоположные. И мой ExpandFileNameRelBaseDir будет служить вашим RelToAbs.

  • 0
    Я полагаю, TWin32PathBuffer является array[0..MAX_PATH-1] of char ?
  • 1
    @ Андреас Правильно и спасибо. Всегда весело выбирать фрагменты кода и делать их автономными.
Показать ещё 10 комментариев
5

Я просто сварил это вместе:

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;

Спасибо Андреасу и Дэвиду за то, что обратили мое внимание на Функции управления трассировкой оболочки.

2
TPath.Combine(S1, S2);

Должно быть доступно с Delphi XE.

1

Проверьте, будет ли ваше решение работать с Relative Path To Full Path в случае изменения текущего каталога. Это будет работать:

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;
1

Альтернативное решение для RelToAbs просто:

ExpandFileName(IncludeTrailingPathDelimiter(BasePath) + RelPath)
0

Другая версия RelToAbs (совместимая со всеми версиями Delphi XE).

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;
0

Я не слишком уверен, что это все еще необходимо после 2+ лет, но вот способ получить отношение к абсолютному (Что касается абсолютного относительного, я бы предложил philnext ExtractRelativePath ответ):

Единица: IOUtils

Родитель: TPath

function GetFullPath(const BasePath: string): string;

Он вернет полный, абсолютный путь для данного относительного пути. Если данный путь уже является абсолютным, он просто вернет его как есть.

Вот ссылка на Embarcadero: Получить полный путь

И вот ссылка для Процедуры манипулирования трафиком

Ещё вопросы

Сообщество Overcoder
Наверх
Меню