Витягнути ім'я файлу з шляху, незалежно від формату OS / path


794

Яку бібліотеку 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

Відповіді:


781

Використання 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 Щоб ми могли використовувати відповідь Странака, і це надійно? ( "Використання os.path.split або os.path.basename як інші пропонують працювати не у всіх випадках: якщо ви запускаєте скрипт в Linux і намагаєтеся обробити класичний шлях у стилі Windows, він не вдасться" - - цитата з поста Лоріца - і я не розумію, чи стосується це попередження відповідь Странака, чи ні).
John cj

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 не може вивести лише ім'я файлу. Отже, imho, ntpath - це шлях.
Харабек

1250

Насправді є функція, яка повертає саме те, що ви хочете

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

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: \\ Користувачі \\ Dell \\ Настільний \\ Кнопка \\ ProjectShadow \\ \\ .py "-" \ b "- це спеціальний символ (я думаю, що система" дзвоник "), подібний до того, як \ r або \ n означають повернення нової лінії / перевезення. Префіксація рядка з r "C: \ ..." означає використовувати поданий вихідний вхід
Брюс Ляммон

87

У пітоні 3

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

3.4 до 3.6 або пізнішої версії, залежно від того, які саме елементи шляху ви використовуєте.
LightCC

8
Ви також можете скористатися Path ("деякий / шлях / до / file.dat"). ствол, щоб отримати ім'я файлу без розширення файлу
s2t2

47
import os
head, tail = os.path.split('path/to/file.exe')

хвіст - це те, що ви хочете, ім'я файлу.

Докладніше див. Документи до модуля python os


13
Просто щоб інші користувачі були обережні, це повертається "", якщо шляхи закінчуються на "/" або "\"
BuZz


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/'взагалі
неправдиве

1
@fmaas os.path.basename - суто функція обробки рядків. Не байдуже, чи існує файл чи це файл чи dir. os.path.basename("a/b/c/")повертається ""через проділ.
Lauritz V. Thaulow

lazyrти правий! Я про це не думав. Чи було б безпечно це зробити path = path.replace('\\', '/')?
Лижі

@Skirmantas Я думаю, але це не вірно. Я думаю, що обробку контуру потрібно проводити за допомогою вбудованих інструментів, які були зроблені для роботи. Доріжок набагато більше, ніж очі.
Lauritz V. Thaulow

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

це повернеться: paint.exe

змінити значення sep розбитої функції стосовно вашого шляху чи ОС.


Ця відповідь мені сподобалась, але чому б не просто зробити наступне? fname = str(path).split('/')[-1]
asultan904

10

Якщо ви хочете отримати ім'я файлу автоматично, ви можете зробити це

import glob

for f in glob.glob('/your/path/*'):
    print(os.path.split(f)[-1])

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

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'[^\\/]+(?![\\/])$'. Для "регулярного виклику, оскарженого", це змінює позитивну пряму позицію вперед для деякого роду косою рисою на негативну вперед лококахед, внаслідок чого назви шляхів, що закінчуються зазначеною косою рисою, не повертають нічого замість останнього підкаталогу в імені шляху. Звичайно, немає гарантії, що потенційне ім'я файлу насправді посилається на файл і для цього 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

Регекс можна перевірити тут .


ви використовуєте re, чому б не os модуль?
Саурабх Чандра Патель

@SaurabhChandraPatel минуло давно. Якщо я добре пам’ятаю, в цьому випадку регекс використовується як рішення між платформами. Наприклад, ви можете обробляти імена файлів 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]

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


2
Що робити, якщо ім'я файлу чи каталог містить пробіл?
kriss

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

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, який може бути корисним для порівняння підпутрів на різних платформах.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.