내 앱에는 OpenFileDialog를 통해 사용자가로드 한 파일 경로를 표시하는 부분이 있습니다. 전체 경로를 표시하는 데 너무 많은 공간을 차지하고 있지만 모호 할 수 있으므로 파일 이름 만 표시하고 싶지 않습니다. 따라서 assembly / exe 디렉토리에 상대적인 파일 경로를 표시하고 싶습니다.
예를 들어, 어셈블리는에 C:\Program Files\Dummy Folder\MyProgram
있고 파일은 C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat
그때 보여주고 싶습니다 .\Data\datafile1.dat
. 파일이에 있으면 C:\Program Files\Dummy Folder\datafile1.dat
내가 원할 것 ..\datafile1.dat
입니다. 그러나 파일이 루트 디렉토리 또는 루트 아래 1 개의 디렉토리에 있으면 전체 경로를 표시하십시오.
어떤 솔루션을 추천 하시겠습니까? 정규식?
기본적으로 화면 공간을 너무 많이 차지하지 않으면 서 유용한 파일 경로 정보를 표시하고 싶습니다.
편집 : 조금 더 명확히하기 위해. 이 솔루션의 목적은 사용자 또는 내가 어느 파일을 마지막으로로드했는지, 그리고 어느 디렉토리에서 왔는지 알 수 있도록 돕는 것입니다. 읽기 전용 텍스트 상자를 사용하여 경로를 표시하고 있습니다. 대부분의 경우 파일 경로는 텍스트 상자의 표시 공간보다 훨씬 깁니다. 경로는 유익하지만 더 많은 화면 공간을 차지할만큼 중요하지는 않습니다.
Alex Brault의 의견은 좋았으며 Jonathan Leffler도 마찬가지입니다. DavidK가 제공하는 Win32 기능은 문제의 일부만이 아니라 문제의 일부에 대해서만 도움이됩니다. James Newton-King 솔루션에 관해서는 나중에 자유 시간에 시도해 볼 것입니다.
답변
.NET Core 2.0에는 Path.GetRelativePath
그렇지 않습니다.
/// <summary>
/// Creates a relative path from one file or folder to another.
/// </summary>
/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
/// <returns>The relative path from the start directory to the end path or <c>toPath</c> if the paths are not related.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="UriFormatException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public static String MakeRelativePath(String fromPath, String toPath)
{
if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath");
if (String.IsNullOrEmpty(toPath)) throw new ArgumentNullException("toPath");
Uri fromUri = new Uri(fromPath);
Uri toUri = new Uri(toPath);
if (fromUri.Scheme != toUri.Scheme) { return toPath; } // path can't be made relative.
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
String relativePath = Uri.UnescapeDataString(relativeUri.ToString());
if (toUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
return relativePath;
}
답변
질문에 조금 늦었지만이 기능도 필요했습니다. DavidK에 동의 합니다.이를 제공 하는 내장 API 함수 가 있으므로이를 사용해야합니다. 여기에 관리 래퍼가 있습니다.
public static string GetRelativePath(string fromPath, string toPath)
{
int fromAttr = GetPathAttribute(fromPath);
int toAttr = GetPathAttribute(toPath);
StringBuilder path = new StringBuilder(260); // MAX_PATH
if(PathRelativePathTo(
path,
fromPath,
fromAttr,
toPath,
toAttr) == 0)
{
throw new ArgumentException("Paths must have a common prefix");
}
return path.ToString();
}
private static int GetPathAttribute(string path)
{
DirectoryInfo di = new DirectoryInfo(path);
if (di.Exists)
{
return FILE_ATTRIBUTE_DIRECTORY;
}
FileInfo fi = new FileInfo(path);
if(fi.Exists)
{
return FILE_ATTRIBUTE_NORMAL;
}
throw new FileNotFoundException();
}
private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
private const int FILE_ATTRIBUTE_NORMAL = 0x80;
[DllImport("shlwapi.dll", SetLastError = true)]
private static extern int PathRelativePathTo(StringBuilder pszPath,
string pszFrom, int dwAttrFrom, string pszTo, int dwAttrTo);
답변
.NET Core 2.0 답변
.NET Core 2.0에는 다음 과 같이 사용할 수있는 Path.GetRelativePath 가 있습니다.
var relativePath = Path.GetRelativePath(
@"C:\Program Files\Dummy Folder\MyProgram",
@"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");
위의 예에서 relativePath
변수는 Data\datafile1.dat
입니다.
대체 .NET 답변
/
경로가 디렉토리 경로 인 경우 파일 경로가 슬래시 문자 ( )로 끝나지 않으면 @Dave의 솔루션이 작동하지 않습니다 . 내 솔루션은 그 문제를 해결하고 Uri.UriSchemeFile
하드 코딩 대신 상수를 사용 "FILE"
합니다.
/// <summary>
/// Creates a relative path from one file or folder to another.
/// </summary>
/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
/// <returns>The relative path from the start directory to the end path.</returns>
/// <exception cref="ArgumentNullException"><paramref name="fromPath"/> or <paramref name="toPath"/> is <c>null</c>.</exception>
/// <exception cref="UriFormatException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public static string GetRelativePath(string fromPath, string toPath)
{
if (string.IsNullOrEmpty(fromPath))
{
throw new ArgumentNullException("fromPath");
}
if (string.IsNullOrEmpty(toPath))
{
throw new ArgumentNullException("toPath");
}
Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath));
Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath));
if (fromUri.Scheme != toUri.Scheme)
{
return toPath;
}
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
return relativePath;
}
private static string AppendDirectorySeparatorChar(string path)
{
// Append a slash only if the path is a directory and does not have a slash.
if (!Path.HasExtension(path) &&
!path.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
return path + Path.DirectorySeparatorChar;
}
return path;
}
Windows Interop 답변
상대 경로를 찾는 데 사용할 수있는 PathRelativePathToA 라는 Windows API 가 있습니다. 함수에 전달하는 파일 또는 디렉토리 경로가 있어야 작동합니다.
var relativePath = PathExtended.GetRelativePath(
@"C:\Program Files\Dummy Folder\MyProgram",
@"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");
public static class PathExtended
{
private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
private const int FILE_ATTRIBUTE_NORMAL = 0x80;
private const int MaximumPath = 260;
public static string GetRelativePath(string fromPath, string toPath)
{
var fromAttribute = GetPathAttribute(fromPath);
var toAttribute = GetPathAttribute(toPath);
var stringBuilder = new StringBuilder(MaximumPath);
if (PathRelativePathTo(
stringBuilder,
fromPath,
fromAttribute,
toPath,
toAttribute) == 0)
{
throw new ArgumentException("Paths must have a common prefix.");
}
return stringBuilder.ToString();
}
private static int GetPathAttribute(string path)
{
var directory = new DirectoryInfo(path);
if (directory.Exists)
{
return FILE_ATTRIBUTE_DIRECTORY;
}
var file = new FileInfo(path);
if (file.Exists)
{
return FILE_ATTRIBUTE_NORMAL;
}
throw new FileNotFoundException(
"A file or directory with the specified path was not found.",
path);
}
[DllImport("shlwapi.dll", SetLastError = true)]
private static extern int PathRelativePathTo(
StringBuilder pszPath,
string pszFrom,
int dwAttrFrom,
string pszTo,
int dwAttrTo);
}
답변
shlwapi.dll에는 Win32 (C ++) 함수가 있으며 원하는 기능을 정확하게 수행합니다. PathRelativePathTo()
P / Invoke 이외의 .NET에서 액세스 할 수있는 방법을 모르겠습니다.
답변
.NET Core 2.0을Path.GetRelativePath()
사용하는 경우 다음과 같은 특정 기능을 제공합니다.
var relativeTo = @"C:\Program Files\Dummy Folder\MyProgram";
var path = @"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat";
string relativePath = System.IO.Path.GetRelativePath(relativeTo, path);
System.Console.WriteLine(relativePath);
// output --> Data\datafile1.dat
그렇지 않으면 v4.7부터 .NET 전체 프레임 워크의 경우 다른 제안 된 답변 중 하나를 사용하는 것이 좋습니다.
답변
나는 이것을 과거에 사용했습니다.
/// <summary>
/// Creates a relative path from one file
/// or folder to another.
/// </summary>
/// <param name="fromDirectory">
/// Contains the directory that defines the
/// start of the relative path.
/// </param>
/// <param name="toPath">
/// Contains the path that defines the
/// endpoint of the relative path.
/// </param>
/// <returns>
/// The relative path from the start
/// directory to the end path.
/// </returns>
/// <exception cref="ArgumentNullException"></exception>
public static string MakeRelative(string fromDirectory, string toPath)
{
if (fromDirectory == null)
throw new ArgumentNullException("fromDirectory");
if (toPath == null)
throw new ArgumentNullException("toPath");
bool isRooted = (Path.IsPathRooted(fromDirectory) && Path.IsPathRooted(toPath));
if (isRooted)
{
bool isDifferentRoot = (string.Compare(Path.GetPathRoot(fromDirectory), Path.GetPathRoot(toPath), true) != 0);
if (isDifferentRoot)
return toPath;
}
List<string> relativePath = new List<string>();
string[] fromDirectories = fromDirectory.Split(Path.DirectorySeparatorChar);
string[] toDirectories = toPath.Split(Path.DirectorySeparatorChar);
int length = Math.Min(fromDirectories.Length, toDirectories.Length);
int lastCommonRoot = -1;
// find common root
for (int x = 0; x < length; x++)
{
if (string.Compare(fromDirectories[x], toDirectories[x], true) != 0)
break;
lastCommonRoot = x;
}
if (lastCommonRoot == -1)
return toPath;
// add relative folders in from path
for (int x = lastCommonRoot + 1; x < fromDirectories.Length; x++)
{
if (fromDirectories[x].Length > 0)
relativePath.Add("..");
}
// add to folders to path
for (int x = lastCommonRoot + 1; x < toDirectories.Length; x++)
{
relativePath.Add(toDirectories[x]);
}
// create relative path
string[] relativeParts = new string[relativePath.Count];
relativePath.CopyTo(relativeParts, 0);
string newPath = string.Join(Path.DirectorySeparatorChar.ToString(), relativeParts);
return newPath;
}
답변
Alex Brault가 특히 Windows에서 지적한 것처럼 절대 경로 (드라이브 문자 및 모두 포함)는 분명하고 종종 더 좋습니다.
OpenFileDialog가 일반적인 트리 브라우저 구조를 사용해서는 안됩니까?
일부 명명법을 얻기 위해 RefDir 은 경로를 지정하려는 상대 디렉토리입니다. AbsName은 매핑 할 것을 절대 경로 이름입니다; 상기 RelPath는 얻어진 상대 경로이다.
일치하는 다음 옵션 중 첫 번째 옵션을 선택하십시오.
- 다른 드라이브 문자가있는 경우 RefDir에서 AbsName까지의 상대 경로가 없습니다. AbsName을 사용해야합니다.
- AbsName이 RefDir의 하위 디렉토리에 있거나 RefDir 내의 파일 인 경우 AbsName의 시작 부분에서 RefDir을 제거하여 RelPath를 작성하십시오. 선택적으로 “./”(또는 Windows에 있으므로 “. \”)를 앞에 추가하십시오.
- RefDir 및 AbsName의 가장 긴 공통 접두사를 찾습니다 (여기서 D : \ Abc \ Def 및 D : \ Abc \ Default는 가장 긴 공통 접두사로 D : \ Abc를 공유합니다. 가장 긴 공통 이름이 아닌 이름 구성 요소의 매핑이어야 함) 하위 문자열); 그것을 LCP라고 부릅니다. AbsName 및 RefDir에서 LCP를 제거하십시오. (RefDir-LCP)에 남아있는 각 경로 구성 요소에 대해 “.. \”를 (AbsName-LCP) 앞에 붙여 RelPath를 생성하십시오.
마지막 규칙 (물론 가장 복잡한 규칙)을 설명하려면 다음과 같이 시작하십시오.
RefDir = D:\Abc\Def\Ghi
AbsName = D:\Abc\Default\Karma\Crucible
그때
LCP = D:\Abc
(RefDir - LCP) = Def\Ghi
(Absname - LCP) = Default\Karma\Crucible
RelPath = ..\..\Default\Karma\Crucible
입력하는 동안 DavidK는이 기능이 가장 먼저 필요하지 않으며이 작업을 수행하는 표준 기능이 있음을 나타내는 답변을 작성했습니다. 그걸 써. 그러나 첫 번째 원칙을 통해 자신의 길을 생각할 수있는 데에는 아무런 해가 없습니다.
유닉스 시스템이 드라이브 문자를 지원하지 않는다는 점을 제외하고 (모든 것이 항상 동일한 루트 디렉토리 아래에 있으므로 첫 번째 글 머리 기호는 관련이 없음) 유닉스에서도 동일한 기술을 사용할 수 있습니다.
![](http://daplus.net/wp-content/uploads/2023/04/coupang_part-e1630022808943-2.png)