web-dev-qa-db-fra.com

Créer un nom de fichier (sain / sûr) à partir de n'importe quelle chaîne (non sécurisée)

Je veux créer un nom de fichier sain/sûr (c'est-à-dire quelque peu lisible, pas de caractères "étranges", etc.) à partir d'une chaîne Unicode aléatoire (mich peut contenir n'importe quoi).

(Peu importe pour moi que la fonction soit Cocoa, ObjC, Python, etc.)


Bien sûr, il peut y avoir une infinité de personnages qui peuvent être étranges. Ainsi, ce n'est pas vraiment une solution d'avoir une liste noire et d'ajouter de plus en plus à cette liste au fil du temps.

Je pourrais avoir une liste blanche. Cependant, je ne sais pas vraiment comment le définir. [a-zA-Z0-9 .] est un début mais je veux également accepter les caractères unicode qui peuvent être affichés de manière normale.

42
Albert

Python:

"".join([c for c in filename if c.isalpha() or c.isdigit() or c==' ']).rstrip()

cela accepte les caractères Unicode mais supprime les sauts de ligne, etc.

exemple:

filename = u"ad\nbla'{-+\)(ç?"

donne: adblaç

éditerstr.isalnum () fait alphanumérique en une seule étape. - commentaire de queueoverflow ci-dessous. danodonovan a fait allusion à garder un point inclus.

    keepcharacters = (' ','.','_')
    "".join(c for c in filename if c.isalnum() or c in keepcharacters).rstrip()
62
Remi

Mes exigences étaient conservatrices (les noms de fichiers générés devaient être valides sur plusieurs systèmes d'exploitation, y compris certains anciens systèmes d'exploitation mobiles). Je me suis retrouvé avec:

    "".join([c for c in text if re.match(r'\w', c)])

Ce blanc répertorie les caractères alphanumériques (a-z, A-Z, 0-9) et le trait de soulignement. L'expression régulière peut être compilée et mise en cache pour plus d'efficacité, s'il y a beaucoup de chaînes à faire correspondre. Pour mon cas, cela n'aurait pas fait de différence significative.

10
Ngure Nyaga

Il y a quelques réponses raisonnables ici, mais dans mon cas, je veux prendre quelque chose qui est une chaîne qui pourrait avoir des espaces et de la ponctuation et plutôt que de simplement les supprimer, je préfère le remplacer par un soulignement. Même si les espaces sont un caractère de nom de fichier autorisé dans la plupart des systèmes d'exploitation, ils sont problématiques. De plus, dans mon cas, si la chaîne d'origine contenait un point, je ne voulais pas que cela passe dans le nom de fichier, ou cela générerait des "extensions supplémentaires" que je ne voudrais peut-être pas (j'ajoute l'extension moi-même)

def make_safe_filename(s):
    def safe_char(c):
        if c.isalnum():
            return c
        else:
            return "_"
    return "".join(safe_char(c) for c in s).rstrip("_")

print(make_safe_filename( "hello you crazy $#^#& 2579 people!!! : die!!!" ) + ".gif")

impressions:

hello_you_crazy _______ 2579_people ______ die ___. gif

6
uglycoyote

Plus ou moins ce qui a été mentionné ici avec regex, mais en sens inverse (remplacez tout NON répertorié):

>>> import re
>>> filename = u"ad\nbla'{-+\)(ç1?"
>>> re.sub(r'[^\w\d-]','_',filename)
u'ad_bla__-_____1_'
4
Filipe Pina

Aucune solution ici, seulement des problèmes que vous devez considérer:

  • quelle est la longueur minimale maximale de votre nom de fichier? (par exemple, DOS ne prenant en charge que 8 à 11 caractères; la plupart des systèmes d'exploitation ne prennent pas en charge> 256 caractères)

  • quels noms de fichiers sont interdits dans un certain contexte? (Windows ne prend toujours pas en charge l'enregistrement d'un fichier en tant que CON.TXT - voir https://blogs.msdn.Microsoft.com/oldnewthing/20031022-00/?p=4207 )

  • rappelez-vous que . et .. ont des significations spécifiques (répertoire courant/parent) et ne sont donc pas sécuritaires.

  • existe-t-il un risque que les noms de fichiers entrent en collision - soit en raison de la suppression de caractères ou du même nom de fichier utilisé plusieurs fois?

Envisagez-vous simplement de hacher les données et d'utiliser le vidage hexadécimal comme nom de fichier?

3
Dragon

Python:

for c in r'[]/\;,><&*:%=+@!#^()|?^':
    filename = filename.replace(c,'')

(juste un exemple de caractères que vous voudrez supprimer) Le r devant la chaîne garantit que la chaîne est interprétée dans son format brut, vous permettant de supprimer la barre oblique inversée \ ainsi que

Edit: solution regex en Python:

import re
re.sub(r'[]/\;,><&*:%=+@!#^()|?^', '', filename)
2
Remi