2014-11-03 4 views
15

У меня есть путь, который выглядит какУдаление первой папки в пути

/First/Second/Third/Fourth/Fifth 

и я хотел бы, чтобы удалить First от него, получая, таким образом

Second/Third/Fourth/Fifth 

Единственная идея, которую я мог бы прийти то есть использовать рекурсивно os.path.split, но это не кажется оптимальным. Есть ли лучшее решение?

ответ

13

Для этого в модуле os.path ничего не существует. Каждый так часто кто-то предлагает создать функцию splitall, которая возвращает список (или итератор) всех компонентов, но он никогда не получал достаточного сцепления.

Отчасти это связано с тем, что каждый раз, когда кто-либо предлагал добавить новую функциональность в os.path, он вновь зажег давнюю неудовлетворенность общим дизайном библиотеки, что привело к тому, что кто-то предложил новый, более OO-подобный API для пути к устареванию os, clunky API. В 3.4 это, наконец, произошло, с pathlib. И у этого уже есть функциональность, которой не было в os.path. Итак:

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

Или ... вы уверены, что действительно хотите удалить первый компонент, а не сделать это?

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

Если вам нужно сделать, это в 2,6-2,7 или 3,2-3,3, есть backport of pathlib.

Конечно, вы можете использовать манипуляции с строкой, если вы стараетесь нормализовать путь и используете os.path.sep, а также убедитесь, что вы обрабатываете детали с непрямыми путями или системами с буквами диска и ...

Или вы можете просто обернуть свой рекурсивный os.path.split. Что именно «неоптимально» об этом, как только вы его завершите? Это может быть немного медленнее, но мы говорим о наносекундах здесь, на несколько порядков быстрее, чем даже вызов stat на файл. У него будут проблемы с глубиной рекурсии, если у вас есть файловая система с тысячами каталогов, но вы когда-нибудь видели ее? (Если это так, вы всегда можете превратить его в цикл ...) Это займет несколько минут, чтобы обернуть его и написать хорошие модульные тесты, но это то, что вы делаете только один раз и больше не волнуетесь. Итак, честно говоря, если вы не хотите использовать pathlib, вот что я буду делать.

+0

'pathlib' dosent поставляется с python, необходимо установить его – Hackaholic

+0

по производительности вы совершенно правы: мы говорим о наносекундах; это больше я пытаюсь выучить наилучший способ/другие способы сделать это. – meto

+0

@Hackaholic: В ответ объясняется подробно, 'pathlib' поставляется с Python 3.4+, и вы можете установить backport для 2.6-2.7 или 3.2-3.3 , – abarnert

4

Простой подход

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

выход:

Second/Third/Fourth/Fifth 

В этом коде выше я разделить строку. затем присоединился к оставляя 1-ый элемент

Использование itertools.dropwhile:

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

Сначала я думал, что это не сработает на путях, которые начинаются с разделителя путей, потому что вы, похоже, просто лишаете первого символа из строки, но после дальнейшего рассмотрения, что делает первый символ, если вы просто удаляете первый сегмент , +1, но, возможно, что-то в ответе, в котором говорится (или, может быть, комментарий от кого-то, кого вы помогли) – iLoveTux

+0

@iLoveTux сделал его более эффективным – Hackaholic

8

Немного похож на другой ответ, воспользовавшись os.path:

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

... предполагается, что ваш строка начинается с сепаратором.

+1

Не могли бы вы немного объяснить использование «*» здесь? – Luke

+0

@ Luke * используется для обработки набора, сгенерированного '(x.split (os.path.sep) [2:])' как ключевое слово '* args'. Однако это не сработает, так как путь слишком короткий, поскольку список аргументов будет полностью пустым – asdf

1

Я смотрел, был ли у нас родной способ сделать это, но, похоже, нет.

Я знаю, что эта тема старая, но это то, что я сделал, чтобы получить лучшее решение: Было два принципиально два подхода: использование split() и использование len(). Оба должны были использовать нарезку.

1) Использование сплит()

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)) 

Результат: --- 0.420122861862 секунд ---

* Удаление символ "/" в строке new_path = "/" + "/".... не улучшали производительность слишком много.

2) Использование len(). Этот метод будет работать только если вы предоставите папку, если вы хотите, чтобы удалить

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)) 

Результат: --- 0.199596166611 секунд ---

* Даже при том, что «если», чтобы проверить если путь начинается с имени файла, он был в два раза быстрее первого метода.

Подводя итог: каждый метод имеет про и con. Если вы абсолютно уверены в папке, которую хотите удалить, используйте метод два, в противном случае я рекомендую использовать метод 1, о котором люди упоминали ранее.

Смежные вопросы