Извлечь имя файла из пути, независимо от того, какой формат os / path

795

Какую библиотеку Python можно использовать для извлечения имен файлов из путей, независимо от операционной системы или формата пути?

Например, я хотел бы, чтобы все эти пути возвращали меня c:

a/b/c/
a/b/c
\a\b\c
\a\b\c\
a\b\c
a/b/../../a/b/c/
a/b/../../a/b/c
жужжание
источник

Ответы:

782

Использование os.path.splitили, os.path.basenameкак предлагают другие, не будет работать во всех случаях: если вы запускаете сценарий в Linux и пытаетесь обработать классический путь в стиле Windows, это не удастся.

Пути Windows могут использовать либо обратную косую черту, либо прямую косую черту в качестве разделителя пути. Следовательно, ntpathмодуль (который эквивалентен os.path при запуске в Windows) будет работать для всех (1) путей на всех платформах.

import ntpath
ntpath.basename("a/b/c")

Конечно, если файл заканчивается косой чертой, базовое имя будет пустым, поэтому создайте свою собственную функцию для его обработки:

def path_leaf(path):
    head, tail = ntpath.split(path)
    return tail or ntpath.basename(head)

Проверка:

>>> paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c', 
...     'a/b/../../a/b/c/', 'a/b/../../a/b/c']
>>> [path_leaf(path) for path in paths]
['c', 'c', 'c', 'c', 'c', 'c', 'c']


(1) Есть одно предупреждение: имена файлов в Linux могут содержать обратную косую черту . Таким образом, в linux r'a/b\c'всегда ссылается на файл b\cв aпапке, в то время как в Windows он всегда ссылается на cфайл в bподпапке aпапки. Поэтому, когда в пути используются как прямая, так и обратная косая черта, вам необходимо знать соответствующую платформу, чтобы иметь возможность правильно ее интерпретировать. На практике обычно можно предположить, что это путь к Windows, поскольку в именах файлов Linux обратные слеши редко используются, но имейте это в виду при кодировании, чтобы не создавать случайных дыр в безопасности.

Лауриц В. Таулов
источник
29
в Windows os.pathпросто загружает ntpathмодуль внутри. Используя этот модуль, можно обрабатывать '\\'разделители пути даже на машинах Linux. Для Linux posixpathмодуль (соответственно os.path) упростит операции пути, чтобы разрешить только разделители в стиле posix '/'.
moooeeeep
@moooeeeep Итак, мы могли бы использовать ответ Stranac, и это надежно? ( «Использование os.path.split или os.path.basename, как предлагают другие, не будет работать во всех случаях: если вы запускаете скрипт в Linux и пытаетесь обработать классический путь в стиле Windows, он потерпит неудачу» - - цитата из поста Лорица - и я не понимаю, касается ли это предупреждение ответа Странака или нет).
Джон
3
@ johnc.j. Только когда вам нужно проанализировать пути в стиле Windows (например, r'C:\path\to\file.txt') на компьютере с Linux, вам нужно использовать модуль ntpath. В противном случае вы можете использовать функции из os.path. Это связано с тем, что системы Linux обычно допускают использование символов обратной косой черты в именах файлов (как объяснено в ответе).
moooeeeep
2
Разве ваше решение не эквивалентно os.path.basename(os.path.normpath(path))?
Mr_and_Mrs_D
2
Для будущих посетителей этого вопроса я столкнулся с ситуацией, о которой предупреждал Лауриц, и его решение было единственным, которое сработало. Никакая путаница с os не может вывести только имя файла. Имхо, ntpath - это путь.
Харабек
1250

На самом деле, есть функция, которая возвращает именно то, что вы хотите

import os
print(os.path.basename(your_path))
stranac
источник
22
Если вы хотите обрабатывать пути независимым от ОС способом, то для os.path.basename (u «C: \\ temp \\ bla.txt») вы ожидаете получить «bla.txt». Вопрос не в получении правильного имени файла, а в извлечении имени для пути.
Ади Ройбан
3
В моем поиске Google для поиска имени файла пути этот ответ был самым полезным. Мой вариант использования только в Windows в любом случае.
Боборт
2
os.path.basename(your_path)Это сработало! Я хотел путь сценария: os.path.dirname(os.path.realpath(__file__))и имя скрипта: os.path.basename(os.path.realpath(__file__)). Спасибо!
TheWalkingData
@AdiRoiban Не могли бы вы разработать свой комментарий? Я проверил это на Windows 7, и я на самом деле получаю «bla.txt». Проще говоря, я не вижу никаких проблем (для себя).
Джон cj
10
@ johnc.j. Дело в том, что когда вы пытались сделать это в Linux, вы получите 'C:\\temp\\bla.txt'вместо этого.
moooeeeep
218

os.path.split - это функция, которую вы ищете

head, tail = os.path.split("/tmp/d/a.dat")

>>> print(tail)
a.dat
>>> print(head)
/tmp/d
Якоб Бауэр
источник
40
Просто для того, чтобы другие пользователи были осторожны, возвращается «», если пути заканчиваются на «/» или «\»
BuZz
Когда я пытаюсь «C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py», он возвращает этот «ProjectShadow utton tton» для всего, кроме этого, он возвращает правильный результат
amitnair92
4
@ amitnair92 - Либо сделайте это: r "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" или это: "C: \\ Users \\ Dell \\ Desktop \\ ProjectShadow \\ button \\ button .py "-" \ b "- это специальный символ (я думаю, что это системный колокол), похожий на то, как \ r или \ n означают перевод строки / возврата каретки. Префикс строки с r «C: \ ...» означает использование заданного необработанного ввода
Брюс Ламонд
87

В питоне 3

>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'
Кишан Б
источник
3.4 до 3.6 или более поздней, в зависимости от того, какие именно элементы pathlib вы используете.
LightCC
8
также можно использовать Path («some / path / to / file.dat»). stem для получения имени файла без расширения файла
s2t2
47
import os
head, tail = os.path.split('path/to/file.exe')

хвост это то, что вы хотите, имя файла.

Подробнее смотрите в документации по модулю Python OS.

number5
источник
13
Просто для того, чтобы другие пользователи были осторожны, возвращается «», если пути заканчиваются на «/» или «\»
BuZz
19
import os
file_location = '/srv/volume1/data/eds/eds_report.csv'
file_name = os.path.basename(file_location )  #eds_report.csv
location = os.path.dirname(file_location )    #/srv/volume1/data/eds
Саурабх Чандра Патель
источник
12

В вашем примере вам также нужно убрать косую черту справа и справа, чтобы вернуться c:

>>> import os
>>> path = 'a/b/c/'
>>> path = path.rstrip(os.sep) # strip the slash from the right side
>>> os.path.basename(path)
'c'

Второй уровень:

>>> os.path.filename(os.path.dirname(path))
'b'

Обновление: я думаю lazyr, предоставил правильный ответ. Мой код не будет работать с Windows-подобными путями в Unix-системах и наоборот с Unix-подобными путями в Windows-системах.

лыжа
источник
Ваш ответ не будет работать r"a\b\c"ни для Linux, ни для "a/b/c"Windows.
Лауриц В. Таулов
конечно, os.path.basename(path)будет работать только если os.path.isfile(path)есть True. Поэтому path = 'a/b/c/'вообще не является допустимым именем файла ...
moooeeeep
1
@fmaas os.path.basename - это просто функция обработки строк. Не имеет значения, существует ли файл, является ли он файлом или каталогом. os.path.basename("a/b/c/")возвращается ""из-за косой черты.
Лауриц В. Таулов
lazyrвы правы! Я не думал об этом. Было бы безопасно просто сделать path = path.replace('\\', '/')?
Ski
@ Skirmantas Я полагаю, но это не так. Я думаю, что обработка пути должна выполняться с помощью встроенных инструментов, которые были созданы для этой работы. Есть гораздо больше путей, чем кажется на первый взгляд.
Lauritz V. Thaulow
11
fname = str("C:\Windows\paint.exe").split('\\')[-1:][0]

это вернет: paint.exe

измените значение sep функции split относительно вашего пути или ОС.

Эслам Хамуда
источник
Этот ответ мне понравился, но почему бы просто не сделать следующее? fname = str(path).split('/')[-1]
asultan904
10

Если вы хотите получить имя файла автоматически, вы можете сделать

import glob

for f in glob.glob('/your/path/*'):
    print(os.path.split(f)[-1])
Vinu
источник
8

Если путь к файлу не заканчивается символом "/", а каталоги разделены символом "/", используйте следующий код. Как мы знаем, обычно путь не заканчивается на «/».

import os
path_str = "/var/www/index.html"
print(os.path.basename(path_str))

Но в некоторых случаях, например, URL-адреса заканчиваются на «/», используйте следующий код

import os
path_str = "/home/some_str/last_str/"
split_path = path_str.rsplit("/",1)
print(os.path.basename(split_path[0]))

но когда ваш путь разделен "\", который вы обычно находите в путях Windows, тогда вы можете использовать следующие коды

import os
path_str = "c:\\var\www\index.html"
print(os.path.basename(path_str))

import os
path_str = "c:\\home\some_str\last_str\\"
split_path = path_str.rsplit("\\",1)
print(os.path.basename(split_path[0]))

Вы можете объединить обе функции в одну, проверив тип ОС и вернув результат.

Сантош Кумар Манда
источник
7

Это работает для Linux и Windows, а также со стандартной библиотекой

paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c',
         'a/b/../../a/b/c/', 'a/b/../../a/b/c']

def path_leaf(path):
    return path.strip('/').strip('\\').split('/')[-1].split('\\')[-1]

[path_leaf(path) for path in paths]

Результаты:

['c', 'c', 'c', 'c', 'c', 'c', 'c']
Csabka
источник
6

Вот решение только для регулярных выражений, которое, похоже, работает с любым путем ОС в любой ОС.

Никаких других модулей не требуется, и предварительная обработка также не требуется:

import re

def extract_basename(path):
  """Extracts basename of a given path. Should Work with any OS Path on any OS"""
  basename = re.search(r'[^\\/]+(?=[\\/]?$)', path)
  if basename:
    return basename.group(0)


paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c',
         'a/b/../../a/b/c/', 'a/b/../../a/b/c']

print([extract_basename(path) for path in paths])
# ['c', 'c', 'c', 'c', 'c', 'c', 'c']


extra_paths = ['C:\\', 'alone', '/a/space in filename', 'C:\\multi\nline']

print([extract_basename(path) for path in extra_paths])
# ['C:', 'alone', 'space in filename', 'multi\nline']

Обновить:

Если вы хотите только потенциальное имя файла, если она присутствует (т.е. /a/b/является реж и так c:\windows\), изменить регулярное выражение: r'[^\\/]+(?![\\/])$'. Для "regex challengeed" это изменяет положительный прогноз прямого просмотра для некоторого вида косой черты на отрицательный прямой взгляд вперед, в результате чего имена путей, заканчивающиеся указанным слешем, ничего не возвращают вместо последнего подкаталога в имени пути. Конечно, нет никакой гарантии, что потенциальное имя файла на самом деле относится к файлу и для этого os.path.is_dir()или os.path.is_file()должно быть использовано.

Это будет соответствовать следующим образом:

/a/b/c/             # nothing, pathname ends with the dir 'c'
c:\windows\         # nothing, pathname ends with the dir 'windows'
c:hello.txt         # matches potential filename 'hello.txt'
~it_s_me/.bashrc    # matches potential filename '.bashrc'
c:\windows\system32 # matches potential filename 'system32', except
                    # that is obviously a dir. os.path.is_dir()
                    # should be used to tell us for sure

Регулярное выражение можно проверить здесь .

Эрик Думинил
источник
вы используете ре, почему бы не ос модуль?
Саурабх Чандра Патель
@SaurabhChandraPatel это было давно. Если я правильно помню, в этом случае в качестве кроссплатформенного решения используется regex. Например, вы можете обрабатывать имена файлов Windows на сервере Linux.
Эрик Думинил
5

Может быть, просто мое все в одном решении без каких-либо важных новых (смотрите временный файл для создания временных файлов: D)

import tempfile
abc = tempfile.NamedTemporaryFile(dir='/tmp/')
abc.name
abc.name.replace("/", " ").split()[-1] 

Получение значений abc.nameбудет такой строкой: '/tmp/tmpks5oksk7' я могу заменить /ее пробелом .replace("/", " ")и затем вызвать split(). Это вернет список, и я получу последний элемент списка с[-1]

Не нужно импортировать какой-либо модуль.

Akendo
источник
2
Что если имя файла или каталога содержит пробел?
Крис
1
Как насчет прямого разделения ("/") [- 1]?
Nan
4

Я никогда не видел пути с двойной обратной косой чертой, они существуют? Встроенная функция модуля Python osне работает для тех. Все остальные работают, также предостережение, данное вами с os.path.normpath():

paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c', 
...     'a/b/../../a/b/c/', 'a/b/../../a/b/c', 'a/./b/c', 'a\b/c']
for path in paths:
    os.path.basename(os.path.normpath(path))
вещий
источник
Это не двойные отступы. Это одиночные обратные слеши, и их нужно избегать.
Эрик
3

Разделителем Windows может быть имя файла Unix или путь к Windows. Разделитель Unix может существовать только в пути Unix. Наличие разделителя Unix указывает путь не из Windows.

Следующее зачистит (разделит конечный разделитель) специальным разделителем ОС, затем разделит и вернет самое правое значение. Это некрасиво, но просто основано на предположении выше. Если предположение неверно, пожалуйста, обновите, и я обновлю этот ответ, чтобы соответствовать более точным условиям.

a.rstrip("\\\\" if a.count("/") == 0 else '/').split("\\\\" if a.count("/") == 0 else '/')[-1]

образец кода:

b = ['a/b/c/','a/b/c','\\a\\b\\c','\\a\\b\\c\\','a\\b\\c','a/b/../../a/b/c/','a/b/../../a/b/c']

for a in b:

    print (a, a.rstrip("\\" if a.count("/") == 0 else '/').split("\\" if a.count("/") == 0 else '/')[-1])
dusc2don
источник
1
Кроме того, не стесняйтесь присылать мне указатели о том, как форматировать в этом месте. Потребовалось полдюжины попыток получить образец кода на месте.
dusc2don
1

Для полноты картины, вот pathlibрешение для Python 3.2+:

>>> from pathlib import PureWindowsPath

>>> paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c', 
...          'a/b/../../a/b/c/', 'a/b/../../a/b/c']

>>> [PureWindowsPath(path).name for path in paths]
['c', 'c', 'c', 'c', 'c', 'c', 'c']

Это работает как на Windows, так и на Linux.

Моргот
источник
1

В обоих Python 2 и 3, используя модуль pathlib2 :

import posixpath  # to generate unix paths
from pathlib2 import PurePath, PureWindowsPath, PurePosixPath

def path2unix(path, nojoin=True, fromwinpath=False):
    """From a path given in any format, converts to posix path format
    fromwinpath=True forces the input path to be recognized as a Windows path (useful on Unix machines to unit test Windows paths)"""
    if not path:
        return path
    if fromwinpath:
        pathparts = list(PureWindowsPath(path).parts)
    else:
        pathparts = list(PurePath(path).parts)
    if nojoin:
        return pathparts
    else:
        return posixpath.join(*pathparts)

Применение:

In [9]: path2unix('lala/lolo/haha.dat')
Out[9]: ['lala', 'lolo', 'haha.dat']

In [10]: path2unix(r'C:\lala/lolo/haha.dat')
Out[10]: ['C:\\', 'lala', 'lolo', 'haha.dat']

In [11]: path2unix(r'C:\lala/lolo/haha.dat') # works even with malformatted cases mixing both Windows and Linux path separators
Out[11]: ['C:\\', 'lala', 'lolo', 'haha.dat']

С вашим тестом:

In [12]: testcase = paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c',
    ...: ...     'a/b/../../a/b/c/', 'a/b/../../a/b/c']

In [14]: for t in testcase:
    ...:     print(path2unix(t)[-1])
    ...:
    ...:
c
c
c
c
c
c
c

Идея здесь состоит в том, чтобы преобразовать все пути в единое внутреннее представление pathlib2с различными декодерами в зависимости от платформы. К счастью, pathlib2включает универсальный вызываемый декодер, PurePathкоторый должен работать на любом пути. В случае, если это не работает, вы можете принудительно распознать путь Windows fromwinpath=True. Это разделит входную строку на части, последняя - это тот лист, который вы ищете, отсюда и path2unix(t)[-1].

Если аргумент nojoin=False, путь будет присоединен обратно, так что выходной файл будет просто входной строкой, преобразованной в формат Unix, что может быть полезно для сравнения подпутей на разных платформах.

gaborous
источник