37 votes

Meilleure façon de déterminer si deux chemins de référence font référence au même fichier en C#

Dans le prochain Java7, il y a une nouvelle API pour vérifier si deux objets fichier ont la même référence de fichier.

Existe-t-il des API similaires fournies dans le framework .NET ?

J'ai cherché sur MSDN mais rien ne m'a éclairé.

Je veux que ce soit simple mais je ne veux pas comparer par nom de fichier ce qui pourrait poser des problèmes avec les liens physiques/symboliques et les styles différents de chemin. (par exemple \\?\C:\, C:\).

Je vais simplement empêcher qu'un fichier dupliqué soit glissé et déposé dans ma liste de liens.

33voto

Rasmus Faber Points 24195

As far as I can see (1) (2) (3) (4), la façon dont JDK7 le fait est en appelant GetFileInformationByHandle sur les fichiers et en comparant dwVolumeSerialNumber, nFileIndexHigh et nFileIndexLow.

Par MSDN:

Vous pouvez comparer les membres VolumeSerialNumber et FileIndex renvoyés dans la structure BY_HANDLE_FILE_INFORMATION pour déterminer si deux chemins se rapportent à la même cible; par exemple, vous pouvez comparer deux chemins de fichiers et déterminer s'ils se rapportent au même répertoire.

Je ne pense pas que cette fonction soit encapsulée par .NET, vous devrez donc utiliser P/Invoke.

Cela pourrait fonctionner ou non pour les fichiers réseau. Selon MSDN:

En fonction des composants réseau sous-jacents du système d'exploitation et du type de serveur auquel vous êtes connecté, la fonction GetFileInformationByHandle peut échouer, renvoyer des informations partielles ou des informations complètes pour le fichier donné.

Un test rapide montre que cela fonctionne comme prévu (mêmes valeurs) avec un lien symbolique sur un système Linux connecté via SMB/Samba, mais qu'il ne peut pas détecter qu'un fichier est le même lorsqu'il est accédé via des partages différents pointant vers le même fichier (FileIndex est le même, mais VolumeSerialNumber diffère).

8voto

Lasse V. Karlsen Points 148037

Modifier: Notez que @Rasmus Faber mentionne la fonction [GetFileInformationByHandle](http://msdn.microsoft.com/en-us/library/aa364952(VS.85%29.aspx) dans l'api Win32, et cela fait ce que vous voulez, vérifiez et votez pour sa réponse pour plus d'informations.


Je pense que vous avez besoin d'une fonction de système d'exploitation pour vous donner les informations que vous voulez, sinon vous aurez quelques faux négatifs quelle que soit la méthode que vous utilisez.

Par exemple, est-ce que ces deux références pointent vers le même fichier ?

  • \server\share\path\filename.txt
  • \server\d$\temp\path\filename.txt

Je vérifierais à quel point il est crucial pour vous de ne pas avoir de doublons dans votre liste, puis je ferais de mon mieux.

Cela dit, il existe une méthode dans la classe Path qui peut faire une partie du travail : Path.GetFullPath, elle va au moins étendre le chemin vers les noms longs, selon la structure existante. Ensuite, il vous suffit de comparer les chaînes de caractères. Ce ne sera pas infaillible cependant, et ne gérera pas les deux liens ci-dessus dans mon exemple.

2voto

JaredPar Points 333733

Réponse : Il n'existe pas de méthode infaillible pour comparer deux chemins d'accès de chaîne afin de déterminer s'ils pointent vers le même fichier.

La raison principale est que des chemins d'accès apparemment sans lien peuvent pointer exactement vers le même fichier en raison de redirections du système de fichiers (jonctions, liens symboliques, etc.). Par exemple

"d: \temp\foo.txt" "c: \othertemp\foo.txt"

Ces chemins d'accès peuvent potentiellement pointer vers le même fichier. Ce cas élimine clairement toute fonction de comparaison de chaîne comme base pour déterminer si deux chemins pointent vers le même fichier.

Le niveau suivant consiste à comparer les informations du fichier OS. Ouvrez le fichier pour deux chemins et comparez les informations de la poignée. Sous Windows, cela peut être fait avec GetFileInformationByHandle. Lucian Wischik a fait un excellent post sur ce sujet ici.

Cependant, il y a encore un problème avec cette approche. Cela ne fonctionne que si le compte utilisateur effectuant la vérification est capable d'ouvrir les deux fichiers en lecture. Il existe de nombreux éléments qui peuvent empêcher un utilisateur d'ouvrir l'un ou l'autre des fichiers. Y compris mais sans s'y limiter ...

  • Manque de permissions suffisantes pour le fichier
  • Manque de permissions suffisantes pour un répertoire dans le chemin du fichier
  • Modification du système de fichiers survenant entre l'ouverture du premier fichier et du second, comme une déconnexion du réseau.

Lorsque vous commencez à examiner tous ces problèmes, vous commencez à comprendre pourquoi Windows ne fournit pas de méthode pour déterminer si deux chemins sont les mêmes. Ce n'est tout simplement pas une question facile/possible à répondre.

1voto

tuinstoel Points 6329

Tout d'abord, j'ai pensé que c'était vraiment facile mais cela ne fonctionne pas:

  string fileName1 = @"c:\vobp.log";
  string fileName2 = @"c:\vobp.log".ToUpper();
  FileInfo fileInfo1 = new FileInfo(fileName1);
  FileInfo fileInfo2 = new FileInfo(fileName2);

  if (!fileInfo1.Exists || !fileInfo2.Exists)
  {
    throw new Exception("l'un des fichiers n'existe pas");
  }

  if (fileInfo1.FullName == fileInfo2.FullName)
  {
    MessageBox.Show("égal"); 
  }

Peut-être que cette bibliothèque peut aider http://www.codeplex.com/FileDirectoryPath. Je ne l'ai pas utilisée moi-même.

éditer: Voir cet exemple sur ce site:

  //
  // Comparaison des chemins
  //
  filePathAbsolute1 = new FilePathAbsolute(@"C:/Dir1\\File.txt");
  filePathAbsolute2 = new FilePathAbsolute(@"C:\DIR1\FILE.TXT");
  Debug.Assert(filePathAbsolute1.Equals(filePathAbsolute2));
  Debug.Assert(filePathAbsolute1 == filePathAbsolute2);

0voto

Alexis Wilke Points 3600

Si vous avez besoin de comparer les mêmes noms de fichiers encore et encore, je vous suggère de regarder comment canonaliser ces noms.

Sous un système Unix, il y a la fonction realpath() qui canonalise votre chemin. Je pense que c'est généralement le meilleur choix si vous avez un chemin complex. Cependant, il est susceptible d'échouer sur des volumes montés via des connexions réseau.

Cependant, basé sur l'approche realpath(), si vous souhaitez prendre en charge plusieurs volumes y compris des volumes réseau, vous pourriez écrire votre propre fonction qui vérifie chaque nom de répertoire dans un chemin et si cela fait référence à un volume, alors déterminez si la référence au volume dans les deux chemins est la même. Cela étant dit, le point de montage peut être différent (c'est-à-dire que le chemin sur le volume de destination peut ne pas être la racine de ce volume) donc ce n'est pas si facile de résoudre tous les problèmes en cours de route, mais c'est définitivement possible (sinon comment cela fonctionnerait-il en premier lieu ?!)

Une fois que les noms de fichiers sont correctement canonalisés, une simple comparaison de chaîne de caractères vous donne la réponse correcte.

La réponse de Rasmus est probablement la manière la plus rapide si vous n'avez pas besoin de comparer les mêmes noms de fichiers encore et encore.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X