แตกชื่อไฟล์จากพา ธ ไม่ว่าจะเป็นรูปแบบระบบปฏิบัติการ / พา ธ ใด


794

ห้องสมุดไพ ธ อนใดที่ฉันสามารถใช้เพื่อแยกชื่อไฟล์จากพา ธ ไม่ว่าระบบปฏิบัติการหรือรูปแบบพา ธ จะเป็นอย่างไร

ตัวอย่างเช่นฉันต้องการเส้นทางเหล่านี้ทั้งหมดเพื่อส่งคืนฉัน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 r'a/b\c'หมายถึงไฟล์b\cในaโฟลเดอร์เสมอในขณะที่บน Windows มันจะอ้างถึงcไฟล์ในbโฟลเดอร์ย่อยของaโฟลเดอร์เสมอ ดังนั้นเมื่อมีการใช้ทั้งเครื่องหมายสแลชไปข้างหน้าและข้างหลังในพา ธ คุณต้องรู้แพลตฟอร์มที่เกี่ยวข้องเพื่อให้สามารถตีความได้อย่างถูกต้อง ในทางปฏิบัติมันมักจะปลอดภัยที่จะถือว่าเป็นเส้นทางของ windows เนื่องจากแบ็กสแลชมักไม่ค่อยใช้ในชื่อไฟล์ของ Linux แต่ควรคำนึงถึงเรื่องนี้เมื่อคุณใช้โค้ดเพื่อที่คุณจะไม่สร้างช่องโหว่ความปลอดภัยโดยไม่ตั้งใจ


29
บน Windows os.pathเพียงแค่โหลดntpathโมดูลภายใน การใช้โมดูลนี้เป็นไปได้ที่จะจัดการกับ'\\'ตัวคั่นพา ธ แม้บนเครื่อง Linux สำหรับ Linux posixpathโมดูล (resp. os.path) จะทำให้การทำงานของพา ธ ง่ายขึ้นเพื่ออนุญาตให้มี'/'ตัวคั่นสไตล์ posix เท่านั้น
moooeeeep

@moooeeeep ดังนั้นเราจึงสามารถใช้คำตอบของ Stranac และเชื่อถือได้? ( "การใช้ os.path.split หรือ os.path.basename อย่างที่คนอื่นแนะนำไม่สามารถใช้ได้ในทุกกรณี: หากคุณใช้งานสคริปต์บน Linux และพยายามประมวลผลเส้นทางแบบคลาสสิกของ Windows มันจะล้มเหลว" - - ใบเสนอราคามาจากโพสต์ของ Lauritz และฉันไม่เข้าใจคำเตือนนี้เกี่ยวข้องกับคำตอบของ Stranac หรือไม่)
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
สำหรับสิ่งที่คุ้มค่าสำหรับผู้เข้าชมในอนาคตสำหรับคำถามนี้ฉันวิ่งเข้าไปในสถานการณ์ที่ Lauritz เตือนและทางออกของเขาเป็นเพียงสิ่งเดียวที่ใช้ได้ผล ไม่มีการ จำกัด จำนวนระบบปฏิบัติการที่สามารถส่งออกได้เพียงชื่อไฟล์ ดังนั้น imho, ntpath คือวิธีที่จะไป
Harabeck

1250

ที่จริงแล้วมีฟังก์ชั่นที่คืนสิ่งที่คุณต้องการ

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

22
หากคุณต้องการประมวลผลพา ธ ด้วยวิธี OS อิสระดังนั้นสำหรับ os.path.basename (u "C: \\ temp \\ bla.txt") คุณคาดหวังว่าจะได้รับ 'bla.txt' คำถามไม่ได้เกี่ยวกับการได้รับชื่อไฟล์ที่ถูกต้อง แต่การแยกชื่อสำหรับเส้นทาง
Adi Roiban

3
ในการค้นหา Google ของฉันเพื่อค้นหาชื่อไฟล์ของเส้นทางคำตอบนี้มีประโยชน์มากที่สุด กรณีการใช้งานของฉันมีเฉพาะใน Windows เท่านั้น
Bobort

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 'เพียงแค่พูดว่าฉันไม่เห็นปัญหาใด ๆ (สำหรับตัวเอง)
john 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 \\ ปุ่ม \\ ปุ่ม \\ .py "-" \ b "เป็นอักขระพิเศษ (ระบบ 'เบลล์' ฉันคิดว่า) คล้ายกับ \ r หรือ \ n หมายถึงการขึ้นบรรทัดใหม่ / การขึ้นบรรทัดใหม่ คำนำหน้าสตริงด้วย r "C: \ ... " หมายถึงใช้ข้อมูลดิบที่กำหนด
Bruce Lamond

87

ในหลาม 3

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

3.4 ถึง 3.6 หรือใหม่กว่าขึ้นอยู่กับรายการ pathlib ที่คุณใช้
LightCC

8
ยังสามารถใช้เส้นทาง ("some / path / to / file.dat"). Stem เพื่อรับชื่อไฟล์โดยไม่มีนามสกุลไฟล์
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


คำตอบของคุณจะไม่ทำงานr"a\b\c"บน linux หรือ"a/b/c"บน windows
Lauritz V. Thaulow

แน่นอนos.path.basename(path)จะทำงานหากมีos.path.isfile(path) Trueดังนั้นจึงpath = 'a/b/c/'ไม่ใช่ชื่อไฟล์ที่ถูกต้องเลย ...
moooeeeep

1
@fmaas os.path.basename เป็นฟังก์ชันการประมวลผลแบบสตริงเท่านั้น มันไม่สนใจว่าไฟล์มีอยู่หรือไม่ว่าจะเป็นไฟล์หรือ dir os.path.basename("a/b/c/")ส่งคืน""เนื่องจาก slash ต่อท้าย
Lauritz V. Thaulow

lazyrคุณพูดถูก! ฉันไม่ได้คิดเรื่องนั้น ปลอดภัยpath = path.replace('\\', '/')ไหมที่จะทำ?
สกี

@Kirmantas ฉันคิดว่า แต่มันไม่รู้สึกขวา ฉันคิดว่าการประมวลผลเส้นทางควรทำด้วยเครื่องมือในตัวที่สร้างขึ้นสำหรับงาน มีมากขึ้นเพื่อเส้นทางกว่าตรงตา
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])

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

นี่เป็นวิธีแก้ปัญหาเฉพาะ regex ซึ่งดูเหมือนว่าจะทำงานกับเส้นทางระบบปฏิบัติการใด ๆ บนระบบปฏิบัติการ

ไม่จำเป็นต้องมีโมดูลอื่นและไม่จำเป็นต้องทำการประมวลผลล่วงหน้า:

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\) เปลี่ยน regex r'[^\\/]+(?![\\/])$'เพื่อ: สำหรับ "regex challenged" สิ่งนี้จะเปลี่ยน lookahead ที่เป็นค่าบวกสำหรับเครื่องหมายทับไปเป็น lookahead ที่เป็นค่าลบทำให้ชื่อพา ธ ที่ลงท้ายด้วยเครื่องหมายทับพูดเพื่อส่งคืนอะไรแทนไดเรกทอรีย่อยสุดท้ายในชื่อพา ธ แน่นอนไม่มีการรับประกันว่าศักยภาพชื่อไฟล์จริงหมายถึงไฟล์และการที่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

regex สามารถทดสอบได้ที่นี่


คุณกำลังใช้อยู่ทำไมไม่ใช้โมดูลระบบปฏิบัติการ
Saurabh Chandra Patel

@SaurabhChandraPatel มันนานมากแล้ว หากฉันจำอย่างถูกต้องจะใช้ regex เป็นโซลูชันข้ามแพลตฟอร์มในกรณีนี้ คุณสามารถประมวลผลชื่อไฟล์ windows บนเซิร์ฟเวอร์ Linux ตัวอย่างเช่น
Eric Duminil

5

อาจเป็นเพียงโซลูชันของฉันทั้งหมดในหนึ่งเดียวโดยไม่สำคัญใหม่ (พิจารณา tempfile สำหรับการสร้างไฟล์ชั่วคราว: 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] คืออะไร
น่าน

4

ฉันไม่เคยเห็นเส้นทางแบ็กสแลชสองครั้งมีอยู่หรือไม่ คุณสมบัติในตัวของโมดูลหลาม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))

นั่นไม่ใช่ backslahes สองเท่า พวกเขาเป็นแบ็กสแลชเดี่ยวและต้องหลบหนี
Eric Duminil

3

ตัวคั่น Windows สามารถอยู่ในชื่อไฟล์ Unix หรือ Windows Path ตัวคั่น 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 ใช้ module 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']

ด้วย testcase ของคุณ:

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ว่าควรทำงานบนเส้นทางใดก็ได้ fromwinpath=Trueในกรณีนี้ไม่ทำงานคุณสามารถบังคับให้รับรู้ของหน้าต่างโดยใช้เส้นทาง path2unix(t)[-1]นี้จะแบ่งสายป้อนเข้าไปในส่วนสุดท้ายคือใบที่คุณกำลังมองหาจึง

หากอาร์กิวเมนต์nojoin=Falseพา ธ จะถูกรวมเข้าด้วยกันเพื่อให้ผลลัพธ์เป็นเพียงอินพุตสตริงที่แปลงเป็นรูปแบบ Unix ซึ่งอาจเป็นประโยชน์ในการเปรียบเทียบพา ธ ย่อยข้ามแพลตฟอร์ม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.