[LINQ] Trouver le nom de fichier suivant disponible

This article is available in english.

Parfois, les exemples les plus simples sont les meilleurs.

Mettons que vous avez un fichier de configuration, et que vous voulez en faire une copie avant de le modifier. Facile, vous copiez le fichier en “filename.bak”. Mais que se passe-t-il si ce fichier existe déjà ? Soit vous l’écrasez, soit vous créez un nom de fichier auto-incrémenté.

Si l’on veut faire ce dernier, il est possible de le faire avec une boucle for. Mais comme vous êtes un bon programmeur fonctionnel, vous allez le faire en utilisant LINQ.

On peut donc écrire ceci :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public static string CreateNewFileName(string filePath)
{
if (!File.Exists(filePath))
return filePath;

// On évite de faire cela pour tous les fichiers
var name = Path.GetFileNameWithoutExtension(filePath);
var extension = Path.GetExtension(filePath);

// Maintenant on cherche le fichier suivant
var fileQuery = from index in Enumerable.Range(2, 10000)

// On construit le nom
let fileName = string.Format("{0} ({1}){2}", name, index, extension)

// Est-ce que le fichier existe ?
where !File.Exists(fileName)

// Non ? On le sélectionne.
select fileName;

// On retourne le premier.
return fileQuery.First();
}

Il faut noter l'utilisation du mot clé “let” qui permet de réutiliser ce que l’on appelle une “range variable”. Dans ce cas, cela permet d’éviter d’appeler string.Format plusieurs fois.

 

Le cas de l’infini

Il y a malgré tout un petit problème dans cette implémentation, qui prend la forme du “10000” arbitraire. Cela peut-être tout à fait correct si vous n’avez pas l’intention de faire 10.000 fichiers de backup. Mais si c’est le cas, pour lever cette limite, on peut écrire l’itérateur suivant :

1
2
3
4
5
6
7
    public static IEnumerable<int> InfiniteRange(int start)
{
while(true)
{
yield return start++;
}
}


Qui fait en sorte de retourner une nouvelle valeur à chaque fois qu’une est demandée. Pour utiliser cette méthode, il faut bien être sur d’avoir une condition de sortie (le fichier n’existe pas, dans l’exemple précédent), sinon vous pourriez bien énumérer jusqu'à la fin des temps... En fait jusqu'à int.MaxValue, pour les nit-pickers, mais .NET 4.0 propose System.Numerics.BigInteger pour être bien sur d’arriver à la fin de temps. On ne sait jamais.

Enfin, pour utiliser cet itérateur, il faut simplement replacer :

1
        var fileQuery = from index in Enumerable.Range(2, 10000)

par

1
        var fileQuery = from index in InfiniteRange(2)

Et c’est terminé !

Publié jeudi 10 juin 2010 23:05 par jay
Classé sous , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

# re: [LINQ] Trouver le nom de fichier suivant disponible @ samedi 12 juin 2010 14:37

Ah la mode linq, c'est terrible :) Pourtant ce n'est pas adapté à tout! Ici en particulier, ça sert à quoi? C'est moins lisible, moins maintenable, plus long, et moins performant (bien que par rapport aux accès disque, ici on s'en fiche un peu). Trois  autres remarques:

1) le code ne valide pas la chaîne de départ

2) le nombre de fichiers maximum par volume (sous NTFS) est de (2^32 -1) donc pas besoin de BigInteger :)

3) le code ne fonctionne que par rapport au répertoire courant, il faudrait faire plutôt ceci pour être tranquille

       public static string CreateNewFileName(string filePath)

       {

           if (filePath == null)

               throw new ArgumentNullException("filePath");

           if (!File.Exists(filePath))

               return filePath;

           var dir = Path.GetDirectoryName(filePath);

           var name = Path.GetFileNameWithoutExtension(filePath);

           var extension = Path.GetExtension(filePath);

           return (from index in InfiniteRange(2)

                           let fileName = Path.Combine(dir, string.Format("{0} ({1}){2}", name, index, extension))

                           where !File.Exists(fileName)

                           select fileName).First();

       }

et une version Linq-Free:

 public static string CreateNewFileNameSansLinq(string filePath)

       {

           if (filePath == null)

               throw new ArgumentNullException("filePath");

           string dir = Path.GetDirectoryName(filePath);

           string name = Path.GetFileNameWithoutExtension(filePath);

           string extension = Path.GetExtension(filePath);

           string newFilePath = filePath;

           int index = 2;

           do

           {

               if (!File.Exists(newFilePath))

                   return newFilePath;

               newFilePath = Path.Combine(dir, string.Format("{0} ({1}){2}", name, index++, extension));

           }

           while (true);

       }

smo

# re: [LINQ] Trouver le nom de fichier suivant disponible @ samedi 12 juin 2010 15:02

1. En effet, il n'y a pas de validation, mais ce n'est qu'un exemple :) L'analyse statique de vs2010 se charge très bien de le rappeler.

2. Certes. La encore, l'exemple du infinite n'est pas pour avoir plus de 10000 fichiers, mais plus pour ne pas avoir de 10000 écrit dans le code.

3. Il m'avait échappé celui la :)

Enfin, la mode linq, c'est une question de gout, et je préfère de plus en plus la manière sans état qui change dans le code que j'écris.

Il est certain que dans ce contexte, on a le système de fichier qui est finalement un gros état que l'on ne gère pas.

L'exemple était pour Linq, et l'appliquer à autre chose que des objets ou de la database.

Tu ne dois pas vraiment apprécier les Reactive Extensions je pense... non ?

jay


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01