web-dev-qa-db-fra.com

Suppression du premier dossier d'un chemin

J'ai un chemin qui ressemble

/First/Second/Third/Fourth/Fifth

et je voudrais en supprimer le First, obtenant ainsi

Second/Third/Fourth/Fifth

La seule idée que j'ai pu trouver est d'utiliser récursivement os.path.split mais cela ne semble pas optimal. Y a-t-il une meilleure solution?

30
meto

Il n'y a vraiment rien dans le os.path module pour ce faire. De temps en temps, quelqu'un suggère de créer une fonction splitall qui renvoie une liste (ou un itérateur) de tous les composants, mais elle n'a jamais gagné suffisamment de traction.

Cela est dû en partie au fait que chaque fois que quelqu'un suggère d'ajouter de nouvelles fonctionnalités à os.path, il a ravivé le mécontentement de longue date à l'égard de la conception générale de la bibliothèque, ce qui a conduit quelqu'un à proposer une nouvelle API, plus proche de l'OO, pour les chemins d'accès à l'OS, API maladroite. En 3.4, cela s'est finalement produit, avec pathlib . Et il a déjà des fonctionnalités qui n'étaient pas dans os.path. Donc:

>>> import pathlib
>>> p = pathlib.Path('/First/Second/Third/Fourth/Fifth')
>>> p.parts[2:]
('Second', 'Third', 'Fourth', 'Fifth')
>>> pathlib.Path(*p.parts[2:])
PosixPath('Second/Third/Fourth/Fifth')

Ou… êtes-vous sûr de vouloir vraiment supprimer le premier composant, plutôt que de le faire?

>>> p.relative_to(*p.parts[:2])
PosixPath('Second/Third/Fourth/Fifth')

Si vous devez le faire en 2.6-2.7 ou 3.2-3.3, il y a backport de pathlib .

Bien sûr, vous pouvez utiliser la manipulation de chaînes, tant que vous faites attention à normaliser le chemin et à utiliser os.path.sep, et pour vous assurer de gérer les détails délicats avec des chemins non absolus ou avec des systèmes avec des lettres de lecteur, et…

Ou vous pouvez simplement terminer votre récursif os.path.split. Qu'est-ce qui est "non optimal" exactement une fois que vous l'avez terminé? Cela peut être un peu plus lent, mais nous parlons ici de nanosecondes, beaucoup plus rapides que d'appeler stat sur un fichier. Il y aura des problèmes de profondeur de récursivité si vous avez un système de fichiers de 1000 répertoires, mais en avez-vous déjà vu un? (Si c'est le cas, vous pouvez toujours le transformer en boucle…) Il faut quelques minutes pour conclure et écrire de bons tests unitaires, mais c'est quelque chose que vous ne faites qu'une seule fois et ne vous inquiétez plus. Donc, honnêtement, si vous ne voulez pas utiliser pathlib, c'est ce que je ferais.

31
abarnert

Un peu comme une autre réponse, profitant d'os.path:

os.path.join(*(x.split(os.path.sep)[2:]))

... en supposant que votre chaîne commence par un séparateur.

17
amyrit

Une approche simple

a = '/First/Second/Third/Fourth/Fifth'
"/".join(a.strip("/").split('/')[1:])

production:

Second/Third/Fourth/Fifth

Dans ce code ci-dessus, j'ai divisé la chaîne. puis rejoint laissant 1er élément

En utilisant itertools.dropwhile:

>>> a = '/First/Second/Third/Fourth/Fifth'
>>> "".join(list(itertools.dropwhile(str.isalnum, a.strip("/"))[1:])
'Second/Third/Fourth/Fifth'
7
Hackaholic

Je cherchais s'il y avait une façon native de le faire, mais il semble que non.

Je sais que ce sujet est ancien, mais voici ce que j'ai fait pour m'amener à la meilleure solution: il y avait deux approches essentiellement deux: utiliser split () et utiliser len (). Les deux devaient utiliser le tranchage.

1) Utilisation de split ()

import time

start_time = time.time()

path = "/folder1/folder2/folder3/file.Zip"
for i in xrange(500000):
    new_path = "/" + "/".join(path.split("/")[2:])

print("--- %s seconds ---" % (time.time() - start_time))

Résultat: --- 0,420122861862 secondes ---

* La suppression du caractère "/" dans la ligne new_path = "/" + "/" .... n'a pas trop amélioré les performances.

2) À l'aide de len (). Cette méthode ne fonctionnera que si vous fournissez le dossier si vous souhaitez le supprimer

import time

start_time = time.time()

path = "/folder1/folder2/folder3/file.Zip"
folder = "/folder1"
for i in xrange(500000):
    if path.startswith(folder):
        a = path[len(folder):]

print("--- %s seconds ---" % (time.time() - start_time))

Résultat: --- 0,199596166611 secondes ---

* Même avec ce "si" pour vérifier si le chemin commence par le nom du fichier, il était deux fois plus rapide que la première méthode.

En résumé: chaque méthode a un pour et un contre. Si vous êtes absolument sûr du dossier que vous souhaitez supprimer, utilisez la deuxième méthode, sinon je recommande d'utiliser la méthode 1 que les gens ici ont mentionnée précédemment.

1
tupan