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

Какую библиотеку 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

Ответ 1

Использование os.path.split или os.path.basename, как показывают другие, не будет работать во всех случаях: если вы запускаете script в 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. Поэтому, когда в пути используются как прямая, так и обратная косая черта, вам нужно знать связанную платформу, чтобы она могла правильно ее интерпретировать. На практике обычно безопасно предполагать, что это путь к окну, поскольку обратные косы редко используются в именах файлов Linux, но имейте это в виду, когда вы кодируете, чтобы не создавать случайные дыры в безопасности.

Ответ 2

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

import os
print(os.path.basename(your_path))

Ответ 3

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

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

>>> print(tail)
a.dat
>>> print(head)
/tmp/d

Ответ 4

В питоне 3

>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'

Ответ 5

import os
head, tail = os.path.split(p)
print tail

Предположим, что p - это входная строка, хвост - это то, что вы хотите.

Подробнее см. python os docs для деталей

Ответ 6

В вашем примере вам также понадобится стричь слэш справа справа, чтобы вернуться 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'

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

Ответ 7

fname = str("C:\Windows\paint.exe").split('\\')[-1:][0]

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

измените значение sep функции разделения для вашего пути или ОС.

Ответ 8

Это работает для 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']

Ответ 9

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

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

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

Ответ 10

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

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

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

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

Ответ 11

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

Я никогда не видел двойных обратных путей, они существуют? Встроенная функция python module 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))

Ответ 13

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

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

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

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

Ответ 14

Разделитель 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])

Ответ 15

Для полноты, вот решение 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.

Ответ 16

В обоих 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] лист, следовательно, path2unix(t)[-1].

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

Ответ 17

1, 4 и 6 не включают файл в вопрос OP, поэтому не указывайте имя файла в любом случае

Ответ 18

filename = path[path.rfind('/')+1:]