web-dev-qa-db-fra.com

Java - Comment savoir si un nom de fichier est valide?

Dans mon application Java, je renomme des fichiers en un nom de fichier fourni dans un paramètre String. Il y a une méthode

boolean OKtoRename(String oldName, String newName)

qui vérifie fondamentalement si newName n'est pas déjà pris par un autre fichier, car je ne voudrais pas enterrer les fichiers existants.

Il m'est maintenant venu à l'esprit que la chaîne newName String ne dénotera peut-être pas un nom de fichier valide. J'ai donc pensé à ajouter cette vérification à la méthode:

if (new File(newName).isFile()) { 
    return false; 
}

Ce qui n'est évidemment pas la bonne façon de le faire, car dans la plupart des cas, le nouveau fichier n'existe pas et, par conséquent, bien qu'il soit OKtoRename, la fonction retourne false.

Je me demandais s'il existe une méthode (je sais qu'il n'y en a pas pour les objets Java.io.File) comme canExist()? Ou devrais-je recourir à regex pour m'assurer que la chaîne newFile ne contient pas de caractères non valides (par exemple?, *, ", :)? Je me demande s'il existe peut-être une fonction cachée quelque part dans le JDK qui indiquerait si une chaîne pourrait éventuellement désigner un nom de fichier valide.

48
Peter Perháč

Utilisez createNewFile() , qui créera le fichier de manière atomique uniquement s’il n’existe pas encore. 

Si le fichier est créé, le nom est valide et n'empiète pas sur un fichier existant. Vous pouvez ensuite ouvrir les fichiers et copier efficacement les données de l’un à l’autre avec les opérations FileChannel.transferXXX.

Il est important de garder à l’esprit que, généralement, le contrôle et la création doivent être atomiques. Si vous vérifiez d'abord si une opération est sécurisée, puis effectuez l'opération séparément, les conditions peuvent avoir changé entre-temps, rendant l'opération non sécurisée.

Des informations supplémentaires sont disponibles à cet article: "Déplacer/copier des opérations en Java".


Mettre à jour:

Depuis cette réponse, les API NIO.2 ont été introduites, ce qui ajoute une interaction accrue avec le système de fichiers.

Supposons que vous ayez un programme interactif et que vous souhaitiez vérifier après chaque frappe si le fichier est potentiellement valide. Par exemple, vous pouvez activer un bouton "Enregistrer" uniquement lorsque l'entrée est valide plutôt que d'afficher une boîte de dialogue d'erreur après avoir appuyé sur "Enregistrer". Créer et assurer la suppression d'un grand nombre de fichiers inutiles, comme l'exige ma suggestion ci-dessus, semble être un gâchis.

Avec NIO.2, vous ne pouvez pas créer une instance Path contenant des caractères non autorisés pour le système de fichiers. Une InvalidPathException est déclenchée dès que vous essayez de créer la Path.

Cependant, il n'y a pas d'API pour valider les noms illégaux composés de caractères valides, comme "PRN" sous Windows. En guise de solution de contournement, des expériences ont montré que l'utilisation d'un nom de fichier illégal générerait une exception distincte lors de la tentative d'accès à des attributs (à l'aide de Files.getLastModifiedTime(), par exemple). 

Si vous spécifiez un nom légal pour un fichier qui existe, vous n'obtenez aucune exception. 

Si vous spécifiez un nom légal pour un fichier qui n'existe pas, il déclenche NoSuchFileException

Si vous spécifiez un nom illégal, FileSystemException est généré. 

Cependant, cela semble très compliqué et pourrait ne pas être fiable sur d'autres systèmes d'exploitation. 

23
erickson

J'ai assemblé une liste de caractères de nom de fichier illégaux (sous UNIX, Mac OS X et Windows) sur la base de recherches en ligne effectuées il y a quelques mois. Si le nouveau nom de fichier en contient, il risque de ne pas être valide sur toutes les plateformes.

private static final char[] ILLEGAL_CHARACTERS = { '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' };

EDIT: Je tiens à souligner que ceci est n'est pas une solution complète: comme l'a fait remarquer un commentateur, même s'il réussit ce test, votre nom de fichier pourrait toujours être un mot clé spécifique à Windows, COM, PRN, etc. Cependant, si votre nom de fichier contient l'un de ces caractères, cela provoquera certainement des problèmes dans un environnement multiplate-forme.

58
Zsolt Török

Ici la manière spécifique du système est suggérée.

public static boolean isFilenameValid(String file) {
  File f = new File(file);
  try {
    f.getCanonicalPath();
    return true;
  } catch (IOException e) {
    return false;
  }
}
17
Maxim Mazin

Si vous développez pour Eclipse, consultez org.Eclipse.core.internal.resources.OS

public abstract class OS {
   private static final String INSTALLED_PLATFORM;

   public static final char[] INVALID_RESOURCE_CHARACTERS;
   private static final String[] INVALID_RESOURCE_BASENAMES;
   private static final String[] INVALID_RESOURCE_FULLNAMES;

   static {
      //find out the OS being used
      //setup the invalid names
      INSTALLED_PLATFORM = Platform.getOS();
      if (INSTALLED_PLATFORM.equals(Platform.OS_WIN32)) {
         //valid names and characters taken from http://msdn.Microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
         INVALID_RESOURCE_CHARACTERS = new char[] {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
         INVALID_RESOURCE_BASENAMES = new String[] {"aux", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ 
               "com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
               "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
         Arrays.sort(INVALID_RESOURCE_BASENAMES);
         //CLOCK$ may be used if an extension is provided
         INVALID_RESOURCE_FULLNAMES = new String[] {"clock$"}; //$NON-NLS-1$
      } else {
         //only front slash and null char are invalid on UNIXes
         //taken from http://www.faqs.org/faqs/unix-faq/faq/part2/section-2.html
         INVALID_RESOURCE_CHARACTERS = new char[] {'/', '\0',};
         INVALID_RESOURCE_BASENAMES = null;
         INVALID_RESOURCE_FULLNAMES = null;
      }
   }

   /**
    * Returns true if the given name is a valid resource name on this operating system,
    * and false otherwise.
    */
   public static boolean isNameValid(String name) {
      //. and .. have special meaning on all platforms
      if (name.equals(".") || name.equals("..")) //$NON-NLS-1$ //$NON-NLS-2$
         return false;
      if (INSTALLED_PLATFORM.equals(Platform.OS_WIN32)) {
         //empty names are not valid
         final int length = name.length();
         if (length == 0)
            return false;
         final char lastChar = name.charAt(length-1);
         // filenames ending in dot are not valid
         if (lastChar == '.')
            return false;
         // file names ending with whitespace are truncated (bug 118997)
         if (Character.isWhitespace(lastChar))
            return false;
         int dot = name.indexOf('.');
         //on windows, filename suffixes are not relevant to name validity
         String basename = dot == -1 ? name : name.substring(0, dot);
         if (Arrays.binarySearch(INVALID_RESOURCE_BASENAMES, basename.toLowerCase()) >= 0)
            return false;
         return Arrays.binarySearch(INVALID_RESOURCE_FULLNAMES, name.toLowerCase()) < 0;
      }
      return true;
   }
}
6
Nick J. R. T.

Voici comment j'ai implémenté ceci:

public boolean isValidFileName(final String aFileName) {
    final File aFile = new File(aFileName);
    boolean isValid = true;
    try {
        if (aFile.createNewFile()) {
            aFile.delete();
        }
    } catch (IOException e) {
        isValid = false;
    }
    return isValid;
}
4
Mosty Mostacho

Pour moi, cela semble être un problème dépendant du système d'exploitation. Vous voudrez peut-être simplement rechercher un caractère non valide dans le nom du fichier. Windows fait cela lorsque vous essayez de renommer le fichier, un message apparaît indiquant qu'un fichier ne peut contenir aucun des caractères suivants:\/: *? <> | Je ne suis pas sûr que votre question soit "Y a-t-il une bibliothèque qui fait le travail pour moi?" dans ce cas, je n'en connais aucun.

3
svachon

Juste quelque chose que j’ai trouvé, dans Java 7 et versions ultérieures, il existe une classe appelée Paths qui possède une méthode appelée get qui prend un ou plusieurs Strings et jette

InvalidPathException - si la chaîne de chemin d'accès ne peut pas être convertie en chemin

3
BrainStorm.exe

En utilisant 

String validName = URLEncoder.encode( fileName , "UTF-8");

File newFile = new File( validName );

Fait le travail. 

Je viens de trouver aujourd'hui. Je ne sais pas si cela fonctionne 100% du temps, mais jusqu'à présent, j'ai pu créer des noms de fichiers valides. 

0
OscarRyz