Je reçois une chaîne d'un processus externe. Je veux utiliser cette chaîne pour créer un nom de fichier, puis écrire dans ce fichier. Voici mon extrait de code pour ce faire:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), s);
PrintWriter currentWriter = new PrintWriter(currentFile);
Si s contient un caractère non valide, tel que «/» dans un système d'exploitation Unix, alors une exception java.io.FileNotFoundException est (à juste titre) lancée.
Comment puis-je encoder la chaîne en toute sécurité afin qu'elle puisse être utilisée comme nom de fichier?
Edit: Ce que j'espère, c'est un appel API qui fait cela pour moi.
Je peux le faire:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), URLEncoder.encode(s, "UTF-8"));
PrintWriter currentWriter = new PrintWriter(currentFile);
Mais je ne suis pas sûr que URLEncoder soit fiable à cet effet.
Réponses:
Si vous voulez que le résultat ressemble au fichier d'origine, SHA-1 ou tout autre schéma de hachage n'est pas la solution. Si les collisions doivent être évitées, le simple remplacement ou la suppression de "mauvais" caractères n'est pas non plus la solution.
Au lieu de cela, vous voulez quelque chose comme ça. (Remarque: cela doit être traité comme un exemple illustratif, pas comme quelque chose à copier et coller.)
Cette solution donne un encodage réversible (sans collision) où les chaînes encodées ressemblent aux chaînes d'origine dans la plupart des cas. Je suppose que vous utilisez des caractères 8 bits.
URLEncoder
fonctionne, mais il a l'inconvénient d'encoder un grand nombre de caractères de noms de fichiers légaux.Si vous voulez une solution non garantie-réversible, supprimez simplement les «mauvais» caractères plutôt que de les remplacer par des séquences d'échappement.
L'inverse du codage ci-dessus doit être tout aussi simple à mettre en œuvre.
la source
Ma suggestion est d'adopter une approche «liste blanche», ce qui signifie ne pas essayer de filtrer les mauvais caractères. Définissez plutôt ce qui est OK. Vous pouvez rejeter le nom de fichier ou le filtrer. Si vous souhaitez le filtrer:
Cela remplace tout caractère qui n'est pas un chiffre, une lettre ou un trait de soulignement par rien. Vous pouvez également les remplacer par un autre caractère (comme un trait de soulignement).
Le problème est que s'il s'agit d'un répertoire partagé, vous ne voulez pas de collision de noms de fichiers. Même si les zones de stockage utilisateur sont séparées par utilisateur, vous pouvez vous retrouver avec un nom de fichier en collision simplement en filtrant les mauvais caractères. Le nom qu'un utilisateur a mis est souvent utile s'il souhaite également le télécharger.
Pour cette raison, j'ai tendance à permettre à l'utilisateur d'entrer ce qu'il veut, de stocker le nom de fichier basé sur un schéma de mon choix (par exemple userId_fileId), puis de stocker le nom de fichier de l'utilisateur dans une table de base de données. De cette façon, vous pouvez l'afficher à nouveau à l'utilisateur, stocker les choses comme vous le souhaitez et ne pas compromettre la sécurité ou effacer d'autres fichiers.
Vous pouvez également hacher le fichier (par exemple, le hachage MD5), mais vous ne pouvez pas lister les fichiers que l'utilisateur a placés (pas avec un nom significatif de toute façon).
EDIT: Correction de regex pour java
la source
"\\W+"
pour l'expression rationnelle en Java. La barre oblique inverse s'applique d'abord à la chaîne elle-même et\W
n'est pas une séquence d'échappement valide. J'ai essayé de modifier la réponse, mais on dirait que quelqu'un a rejeté ma modification :(Cela dépend si l'encodage doit être réversible ou non.
Réversible
Utilisez le codage URL (
java.net.URLEncoder
) pour remplacer les caractères spéciaux par%xx
. Notez que vous prenez soin des cas particuliers où la chaîne est égale.
, égale..
ou vide! ¹ De nombreux programmes utilisent le codage URL pour créer des noms de fichiers, c'est donc une technique standard que tout le monde comprend.Irréversible
Utilisez un hachage (par exemple SHA-1) de la chaîne donnée. Les algorithmes de hachage modernes (et non MD5) peuvent être considérés comme sans collision. En fait, vous aurez une percée dans la cryptographie si vous trouvez une collision.
¹ Vous pouvez gérer les 3 cas spéciaux avec élégance en utilisant un préfixe tel que
"myApp-"
. Si vous mettez le fichier directement dans$HOME
, vous devrez le faire de toute façon pour éviter les conflits avec des fichiers existants tels que ".bashrc".la source
Voici ce que j'utilise:
Ce que cela fait est de remplacer chaque caractère qui n'est pas une lettre, un chiffre, un trait de soulignement ou un point par un trait de soulignement, en utilisant regex.
Cela signifie que quelque chose comme "Comment convertir £ en $" deviendra "How_to_convert___to__". Certes, ce résultat n'est pas très convivial, mais il est sûr et les noms de répertoires / fichiers résultants sont garantis de fonctionner partout. Dans mon cas, le résultat n'est pas montré à l'utilisateur, et n'est donc pas un problème, mais vous voudrez peut-être modifier l'expression régulière pour être plus permissive.
Il convient de noter qu'un autre problème que j'ai rencontré était que j'obtenais parfois des noms identiques (car il est basé sur l'entrée de l'utilisateur), vous devez donc être conscient de cela, car vous ne pouvez pas avoir plusieurs répertoires / fichiers avec le même nom dans un seul répertoire . J'ai juste ajouté l'heure et la date actuelles, et une courte chaîne aléatoire pour éviter cela. (une chaîne aléatoire réelle, pas un hachage du nom de fichier, car des noms de fichiers identiques entraîneront des hachages identiques)
De plus, vous devrez peut-être tronquer ou raccourcir la chaîne résultante, car elle peut dépasser la limite de 255 caractères de certains systèmes.
la source
Pour ceux qui recherchent une solution générale, il peut s'agir de critères courants:
Pour ce faire, nous pouvons utiliser regex pour faire correspondre les caractères illégaux, les encoder en pourcentage , puis contraindre la longueur de la chaîne encodée.
Motifs
Le modèle ci-dessus est basé sur un sous-ensemble conservateur de caractères autorisés dans la spécification POSIX .
Si vous souhaitez autoriser le caractère point, utilisez:
Méfiez-vous simplement des chaînes telles que "." et ".."
Si vous souhaitez éviter les collisions sur les systèmes de fichiers insensibles à la casse, vous devrez échapper les majuscules:
Ou échapper aux lettres minuscules:
Plutôt que d'utiliser une liste blanche, vous pouvez choisir de mettre sur liste noire les caractères réservés pour votre système de fichiers spécifique. EG Cette regex convient aux systèmes de fichiers FAT32:
Longueur
Sur Android, 127 caractères est la limite de sécurité. De nombreux systèmes de fichiers autorisent 255 caractères.
Si vous préférez conserver la queue plutôt que la tête de votre corde, utilisez:
Décodage
Pour reconvertir le nom de fichier dans la chaîne d'origine, utilisez:
Limites
Étant donné que les chaînes plus longues sont tronquées, il existe un risque de collision de noms lors du codage ou de corruption lors du décodage.
la source
Pattern.compile("[^A-Za-z0-9_\\-]")
Essayez d'utiliser l'expression régulière suivante qui remplace chaque caractère de nom de fichier non valide par un espace:
la source
_
ou-
.Choisissez votre poison parmi les options présentées par commons-codec , exemple:
la source
sha1
;sha
est obsolète.Ce n'est probablement pas le moyen le plus efficace, mais montre comment le faire à l'aide de pipelines Java 8:
La solution pourrait être améliorée en créant un collecteur personnalisé qui utilise StringBuilder, afin que vous n'ayez pas à convertir chaque caractère léger en une chaîne lourde.
la source
Vous pouvez supprimer les caractères non valides ('/', '\', '?', '*'), Puis l'utiliser.
la source