2008-11-28 17 views
26

tôi muốn làm giống hệt như trong this question:Bắt tên tập tin thực tế (với vỏ hợp) trên Windows với .NET

hệ thống tập tin Windows là trường hợp nhạy cảm. Cách đặt tên tệp/thư mục (ví dụ: "somefile"), tôi nhận được tên thực tế của tệp/thư mục đó (ví dụ: nó sẽ trả về "SomeFile" nếu Explorer hiển thị tên đó)?

Nhưng tôi cần làm điều đó trong .NET và tôi muốn đường dẫn đầy đủ (D:/Temp/Foobar.xml và không chỉ Foobar.xml).

Tôi thấy rằng FullName trên lớp FileInfo không thực hiện thủ thuật.

Trả lời

21

Tôi có vẻ như là vì NTFS phân biệt chữ hoa chữ thường, nó sẽ luôn chấp nhận đầu vào của bạn một cách chính xác cho dù tên có được đặt đúng hay không.

Cách duy nhất để có được tên đường dẫn chính xác dường như tìm tệp như John Sibly được đề xuất.

Tôi tạo ra một phương pháp mà sẽ mất một đường dẫn (thư mục hoặc tập tin) và trở về phiên bản cased chính xác của nó (cho toàn bộ đường dẫn):

public static string GetExactPathName(string pathName) 
    { 
     if (!(File.Exists(pathName) || Directory.Exists(pathName))) 
      return pathName; 

     var di = new DirectoryInfo(pathName); 

     if (di.Parent != null) { 
      return Path.Combine(
       GetExactPathName(di.Parent.FullName), 
       di.Parent.GetFileSystemInfos(di.Name)[0].Name); 
     } else { 
      return di.Name.ToUpper(); 
     } 
    } 

Dưới đây là một số trường hợp thử nghiệm mà làm việc trên máy tính của tôi :

static void Main(string[] args) 
    { 
     string file1 = @"c:\documents and settings\administrator\ntuser.dat"; 
     string file2 = @"c:\pagefile.sys"; 
     string file3 = @"c:\windows\system32\cmd.exe"; 
     string file4 = @"c:\program files\common files"; 
     string file5 = @"ddd"; 

     Console.WriteLine(GetExactPathName(file1)); 
     Console.WriteLine(GetExactPathName(file2)); 
     Console.WriteLine(GetExactPathName(file3)); 
     Console.WriteLine(GetExactPathName(file4)); 
     Console.WriteLine(GetExactPathName(file5)); 

     Console.ReadLine(); 
    } 

Phương thức sẽ trả lại giá trị được cung cấp nếu tệp không tồn tại.

Có thể có các phương pháp nhanh hơn (điều này sử dụng đệ quy) nhưng tôi không chắc chắn nếu có bất kỳ cách rõ ràng để làm điều đó.

+3

NTFS chắc chắn là trường hợp * nhạy cảm *. Đó là API Windows không nhất quán về nó. –

+1

Nếu bạn thay thế 'return di.Name.ToUpper();' bằng 'return di.FullName.ToUpper();' (FullName), nó sẽ làm việc với các đường dẫn UNC. :-) – ygoe

+0

Tôi thích câu trả lời này, nhưng tôi đã thích nghi nó trong câu trả lời của mình với phương thức TryGetExactPath hỗ trợ đường dẫn UNC, cho tôi biết đường dẫn không tồn tại hay không và sử dụng phép lặp thay vì đệ quy để giảm thiểu các cuộc gọi đến Path.Combine. –

-3

Bạn đã thử lớp DirectoryInfo và Path, họ có thể thực hiện thủ thuật. (Chưa tự mình thử)

0

Sự cố thú vị.

Một cách để làm điều đó là "tìm" tệp dựa trên tên không phân biệt chữ hoa chữ thường và sau đó xem thuộc tính FileInfo.FullName. Tôi đã thử nghiệm điều này bằng cách sử dụng chức năng sau đây và nó cho kết quả cần thiết.

static string GetCaseSensitiveFileName(string filePath) 
{ 
    string caseSensitiveFilePath = null; 

    DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(filePath)); 
    FileInfo[] files = dirInfo.GetFiles(Path.GetFileName(filePath)); 
    if (files.Length > 0) 
    { 
     caseSensitiveFilePath = files[0].FullName; 
    } 

    return caseSensitiveFilePath; 
} 

Bạn cần cẩn thận một chút ở đây - nếu bạn có hai tệp được gọi tên như file.xml và File.xml thì nó sẽ chỉ trả về tên đầu tiên.

+0

Các câu hỏi ban đầu đã tuyên bố rằng nhìn vào FileInfo.FullName không cung cấp các thông tin cần thiết. –

+1

Đúng vậy - nếu bạn chỉ sử dụng lớp FileInfo thì đây là trường hợp. Nhưng nếu bạn tìm lại tệp bằng cách sử dụng DirectoryInfo và GetFiles (đi qua đường dẫn đầy đủ), thì nếu bạn xem FullPath, bạn có thể trả về thông tin bắt buộc. Tôi đã thử nghiệm chức năng mà tôi đã đăng và nó hoạt động. –

+1

Bạn là chính xác, điều này không hoạt động và trả về thông tin chính xác. –

1

Tôi nghĩ cách duy nhất bạn có thể làm điều này là sử dụng cùng một API Win32, cụ thể là phương thức SHGetFileInfo, được đề cập trong câu trả lời được chấp nhận cho câu hỏi bạn tham chiếu. Để thực hiện điều này, bạn sẽ cần sử dụng một số lệnh gọi interop p/invoke. Hãy xem pinvoke.net để biết ví dụ về cách thực hiện điều này và những cấu trúc bổ sung nào bạn sẽ cần.

0

Dường như cách tốt nhất là để lặp qua tất cả các thư mục trong đường dẫn và nhận được mũ thích hợp của họ:

Public Function gfnProperPath(ByVal sPath As String) As String 
    If Not IO.File.Exists(sPath) AndAlso Not IO.Directory.Exists(sPath) Then Return sPath 
    Dim sarSplitPath() As String = sPath.Split("\") 
    Dim sAddPath As String = sarSplitPath(0).ToUpper & "\" 
    For i = 1 To sarSplitPath.Length - 1 
     sPath = sAddPath & "\" & sarSplitPath(i) 
     If IO.File.Exists(sPath) Then 
      Return IO.Directory.GetFiles(sAddPath, sarSplitPath(i), IO.SearchOption.TopDirectoryOnly)(0) 
     ElseIf IO.Directory.Exists(sPath) Then 
      sAddPath = IO.Directory.GetDirectories(sAddPath, sarSplitPath(i), IO.SearchOption.TopDirectoryOnly)(0) 
     End If 
    Next 
    Return sPath 
End Function 
3

trả lời thứ hai của tôi ở đây với một phương pháp không đệ quy. Nó chấp nhận cả hai tập tin và thư mục.
Lần này dịch từ VB đến C#:

private string fnRealCAPS(string sDirOrFile) 
{ 
    string sTmp = ""; 
    foreach (string sPth in sDirOrFile.Split("\\")) { 
     if (string.IsNullOrEmpty(sTmp)) { 
      sTmp = sPth + "\\"; 
      continue; 
     } 
     sTmp = System.IO.Directory.GetFileSystemEntries(sTmp, sPth)[0]; 
    } 
    return sTmp; 
}
+1

Được bình chọn, nhưng ký hiệu Hungary đã khiến tôi khóc :-) – Konamiman

4

Lấy cảm hứng từ câu trả lời của Ivan, đây là một phương pháp mà cũng xử lý thư vỏ ổ đĩa cũng như:

public string FixFilePathCasing(string filePath) 
{ 
    string fullFilePath = Path.GetFullPath(filePath); 

    string fixedPath = ""; 
    foreach(string token in fullFilePath.Split('\\')) 
    { 
     //first token should be drive token 
     if(fixedPath == "") 
     { 
      //fix drive casing 
      string drive = string.Concat(token, "\\"); 
      drive = DriveInfo.GetDrives() 
       .First(driveInfo => driveInfo.Name.Equals(drive, StringComparison.OrdinalIgnoreCase)).Name; 

      fixedPath = drive; 
     } 
     else 
     { 
      fixedPath = Directory.GetFileSystemEntries(fixedPath, token).First(); 
     } 
    } 

    return fixedPath; 
} 
4

tôi thích Yona's answer, nhưng tôi muốn nó đến: đường dẫn

  • Hỗ trợ UNC
  • Nói cho tôi biết nếu đường dẫn không tồn tại
  • Sử dụng lặp lại thay vì đệ quy (vì nó chỉ sử dụng đệ quy đuôi)
  • Giảm thiểu số lượng cuộc gọi đến Path.Combine (để giảm thiểu chuỗi nối).
/// <summary> 
/// Gets the exact case used on the file system for an existing file or directory. 
/// </summary> 
/// <param name="path">A relative or absolute path.</param> 
/// <param name="exactPath">The full path using the correct case if the path exists. Otherwise, null.</param> 
/// <returns>True if the exact path was found. False otherwise.</returns> 
/// <remarks> 
/// This supports drive-lettered paths and UNC paths, but a UNC root 
/// will be returned in title case (e.g., \\Server\Share). 
/// </remarks> 
public static bool TryGetExactPath(string path, out string exactPath) 
{ 
    bool result = false; 
    exactPath = null; 

    // DirectoryInfo accepts either a file path or a directory path, and most of its properties work for either. 
    // However, its Exists property only works for a directory path. 
    DirectoryInfo directory = new DirectoryInfo(path); 
    if (File.Exists(path) || directory.Exists) 
    { 
     List<string> parts = new List<string>(); 

     DirectoryInfo parentDirectory = directory.Parent; 
     while (parentDirectory != null) 
     { 
      FileSystemInfo entry = parentDirectory.EnumerateFileSystemInfos(directory.Name).First(); 
      parts.Add(entry.Name); 

      directory = parentDirectory; 
      parentDirectory = directory.Parent; 
     } 

     // Handle the root part (i.e., drive letter or UNC \\server\share). 
     string root = directory.FullName; 
     if (root.Contains(':')) 
     { 
      root = root.ToUpper(); 
     } 
     else 
     { 
      string[] rootParts = root.Split('\\'); 
      root = string.Join("\\", rootParts.Select(part => CultureInfo.CurrentCulture.TextInfo.ToTitleCase(part))); 
     } 

     parts.Add(root); 
     parts.Reverse(); 
     exactPath = Path.Combine(parts.ToArray()); 
     result = true; 
    } 

    return result; 
} 

Đối với UNC đường dẫn, các trường hợp này vào thư mục gốc (\\ Server \ Share) trong trường hợp tiêu đề chứ không phải là trường hợp chính xác vì nó sẽ là rất nhiều làm việc nhiều hơn để cố gắng xác định máy chủ từ xa chính xác tên trường hợp và tên trường hợp chính xác của cổ phiếu. Nếu bạn muốn thêm hỗ trợ đó, bạn sẽ phải sử dụng các phương thức P/Invoke như NetServerEnumNetShareEnum. Nhưng những thứ đó có thể chậm và chúng không hỗ trợ tính năng lọc phía trước chỉ với máy chủ và chia sẻ các tên mà bạn quan tâm.

Dưới đây là một phương pháp đơn vị thử nghiệm cho TryGetExactPath (sử dụng Visual Studio Testing Extensions):

[TestMethod] 
public void TryGetExactPathNameTest() 
{ 
    string machineName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Environment.MachineName.ToLower()); 
    string[] testPaths = new[] 
     { 
      @"C:\Users\Public\desktop.ini", 
      @"C:\pagefile.sys", 
      @"C:\Windows\System32\cmd.exe", 
      @"C:\Users\Default\NTUSER.DAT", 
      @"C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies", 
      @"C:\Program Files (x86)", 
      @"Does not exist", 
      @"\\Nas\Main\Setups", 
      @"\\Nas\Main\Setups\Microsoft\Visual Studio\VS 2015\vssdk_full.exe", 
      @"\\" + machineName + @"\C$\Windows\System32\ActionCenter.dll", 
      @"..", 
     }; 
    Dictionary<string, string> expectedExactPaths = new Dictionary<string, string>() 
     { 
      { @"..", Path.GetDirectoryName(Environment.CurrentDirectory) }, 
     }; 

    foreach (string testPath in testPaths) 
    { 
     string lowercasePath = testPath.ToLower(); 
     bool expected = File.Exists(lowercasePath) || Directory.Exists(lowercasePath); 
     string exactPath; 
     bool actual = FileUtility.TryGetExactPath(lowercasePath, out exactPath); 
     actual.ShouldEqual(expected); 
     if (actual) 
     { 
      string expectedExactPath; 
      if (expectedExactPaths.TryGetValue(testPath, out expectedExactPath)) 
      { 
       exactPath.ShouldEqual(expectedExactPath); 
      } 
      else 
      { 
       exactPath.ShouldEqual(testPath); 
      } 
     } 
     else 
     { 
      exactPath.ShouldBeNull(); 
     } 
    } 
} 
Các vấn đề liên quan