İşletim sistemi / yol biçimi ne olursa olsun dosya adını yoldan çıkarın


794

İşletim sistemi veya yol biçimi ne olursa olsun dosya adlarını yollardan ayıklamak için hangi Python kitaplığını kullanabilirim?

Örneğin, tüm bu yolların bana dönmesini istiyorum 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

Yanıtlar:


781

os.path.splitVeya os.path.basenamebaşkalarının önerdiği gibi kullanmak her durumda işe yaramaz: komut dosyasını Linux'ta çalıştırıyorsanız ve klasik bir pencere stili yolunu işlemeye çalışırsanız başarısız olur.

Windows yolları, yol ayırıcı olarak ters eğik çizgi veya ileri eğik çizgi kullanabilir. Bu nedenle, ntpathmodül (pencerelerde çalışırken os.path'e eşdeğerdir ) tüm platformlardaki tüm (1) yollar için çalışır.

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

Tabii ki, dosya bir eğik çizgi ile biterse, taban adı boş olacaktır, bu yüzden onunla başa çıkmak için kendi işlevinizi yapın:

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

Doğrulama:

>>> 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) Bir uyarı var: Linux dosya adları ters eğik çizgiler içerebilir . Yani linux üzerinde, r'a/b\c'her zaman dosyaya atıfta b\ciçinde aWindows üzerinde, her zaman ifade eder iken, klasördeki cdosya biçinde alt klasör aklasöründe. Bu nedenle, hem ileri hem de geri eğik çizgiler bir yolda kullanıldığında, doğru bir şekilde yorumlayabilmek için ilişkili platformu bilmeniz gerekir . Uygulamada, ters eğik çizgiler Linux dosya adlarında nadiren kullanıldığından, bunun bir pencere yolu olduğunu varsaymak genellikle güvenlidir, ancak kod yazarken bunu göz önünde bulundurun, böylece yanlışlıkla güvenlik delikleri oluşturmazsınız.


29
Windows'da, modülü dahili olarak os.pathyükler ntpath. Bu modülü kullanarak '\\', Linux makinelerde bile yol ayırıcıları işlemek mümkündür . Linux için posixpathmodül (sırasıyla os.path), yalnızca posix stil '/'ayırıcılarına izin vermek için yol işlemlerini basitleştirecektir .
moooeeeep

@moooeeeep Yani Stranac'ın cevabını kullanabiliriz ve güvenilir midir? ( "Diğerlerinin önerdiği gibi os.path.split veya os.path.basename komutunu kullanmak her durumda çalışmaz: komut dosyasını Linux'ta çalıştırıyorsanız ve klasik bir pencere stili yolunu işlemeye çalışırsanız başarısız olur" - - alıntı Lauritz'in gönderisindendir - ve anlamıyorum, bu uyarı Stranac'ın cevabı ile ilgili mi, değil mi).
john cj

3
@ johnc.j. Yalnızca r'C:\path\to\file.txt'bir Linux makinesinde Windows stil yollarını (örn. ) Ayrıştırmanız gerektiğinde, ntpath modülünü kullanmanız gerekir. Aksi takdirde, os.path işlevini kullanabilirsiniz. Bunun nedeni, Linux sistemlerinin normalde dosya adlarında (yanıtta açıklandığı gibi) ters eğik çizgi karakterlerinin kullanılmasına izin vermesidir.
moooeeeep

2
Çözümünüz eşdeğer değil os.path.basename(os.path.normpath(path))mi?
Mr_and_Mrs_D

2
Bu sorunun gelecekteki ziyaretçileri için değerli olan şey için, Lauritz'in uyardığı durumla karşılaştım ve çözümü işe yarayan tek şeydi. OS ile hiçbir bağlantı sadece dosya adını veremez. Yani imho, ntpath gidilecek yol.
Harabeck

1250

Aslında, tam olarak istediğinizi döndüren bir işlev var

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

22
Yolları işletim sisteminden bağımsız bir şekilde işlemek istiyorsanız, os.path.basename (u "C: \\ temp \\ bla.txt") için 'bla.txt' almayı beklersiniz. Soru, geçerli bir dosya adı almak değil, bir yolun adını çıkarmakla ilgilidir.
Adi Roiban

3
Bir yolun dosya adını bulmak için Google aramamda bu yanıt en yararlısıydı. Kullanım durumum sadece Windows üzerinde.
Bobort

2
os.path.basename(your_path)Bu işe yaradı! : Senaryonun yolunu istedi os.path.dirname(os.path.realpath(__file__))ve komut dosyası adını: os.path.basename(os.path.realpath(__file__)). Teşekkürler!
TheWalkingData

@AdiRoiban Yorumunuzu ayrıntılı olarak açıklar mısınız? Ben Windows 7 üzerinde test edilmiş ve aslında ' "bla.txt olsun Basitçe I (Ben) herhangi bir sorun görmüyorum diyerek..
john cj

10
@ johnc.j. Mesele şu ki, Linux'ta bunu denediğinizde, 'C:\\temp\\bla.txt'bunun yerine elde edersiniz .
moooeeeep

218

os.path.split , aradığınız işlevdir

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

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

40
Diğer kullanıcıların dikkatli olması için, yollar "/" veya "\" ile bitiyorsa "" döndürür
BuZz

Ben çalıştığınızda "C: \ Kullanıcılar \ Dell \ Desktop \ ProjectShadow \ düğmesine \ button.py" döndürür Thi "ProjectShadow basıp bırakın tton" doğru sonuç döndürmek bundan başka her şey için
amitnair92

4
@ amitnair92 - Bunu yapın: r "C: \ Kullanıcılar \ Dell \ Desktop \ ProjectShadow \ button \ button.py" veya bu: "C: \\ Kullanıcılar \\ Dell \\ Masaüstü \\ ProjectShadow \\ düğmesi \\ düğmesi .py "-" \ b ", \ r veya \ n satırsonu / satır başı dönüşünü nasıl ifade ettiğine benzer özel bir karakterdir (bence sistem 'zil'). Dizeye r "C: \ ..."
Bruce Lamond

87

Python 3'te

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

Tam olarak hangi pathlib öğelerini kullandığınıza bağlı olarak 3,4 ila 3,6 veya üstü.
LightCC

8
ayrıca dosya uzantısını almadan dosya
adını

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

kuyruk istediğiniz şeydir, dosya adı.

Bkz piton os modülü belgeler ayrıntı


13
Diğer kullanıcıların dikkatli olması için, yollar "/" veya "\" ile bitiyorsa "" döndürür
BuZz

18
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

Örneğinizde, geri dönmek için sağdan sağa eğik çizgi yapmanız gerekir c:

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

İkinci seviye:

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

güncelleme: Bence lazyrdoğru cevabı verdi. Kodum, unix sistemlerindeki windows benzeri yollarla ve Windows sistemindeki unix benzeri yollarla karşılaştırıldığında işe yaramaz.


Cevabınız r"a\b\c"linux veya "a/b/c"pencerelerde çalışmaz .
Lauritz V. Thaulow

elbette, os.path.basename(path)yalnızca çalışacak os.path.isfile(path)olan True. Bu nedenle path = 'a/b/c/'geçerli bir dosya adı değil ...
moooeeeep

1
@fmaas os.path.basename tamamen bir dize işleme işlevidir. Dosyanın var olup olmadığı ya da bir dosya ya da dizin olması önemli değildir. eğik çizgi nedeniyle os.path.basename("a/b/c/")geri döner "".
Lauritz V. Thaulow

lazyrHaklısın! Bunu düşünmemiştim. Sadece yapmak güvenli olur path = path.replace('\\', '/')mu?
Kayak

@Skirmantas Sanırım, ama doğru gelmiyor. Ben yol işleme iş için yapılmış yerleşik araçlarla yapılması gerektiğini düşünüyorum. Orada çok daha fazla göründüğünden daha yollara.
Lauritz V. Thaulow

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

bu dönecektir: paint.exe

yolunuz veya işletim sisteminizle ilgili split işlevinin sep değerini değiştirin.


Bu beğendiğim cevap, ama neden sadece aşağıdakileri yapmıyoruz? fname = str(path).split('/')[-1]
asultan904

10

Dosya adını otomatik olarak almak istiyorsanız,

import glob

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

8

Dosya yolunuz "/" ile bitmemişse ve dizinler "/" ile ayrılmışsa, aşağıdaki kodu kullanın. Bildiğimiz gibi genel olarak yol "/" ile bitmiyor.

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

Ancak, URL'ler gibi "/" ile biten bazı durumlarda aşağıdaki kodu kullanın

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

ancak yolunuz genellikle Windows yollarında bulduğunuz "\" ile belirtildiğinde aşağıdaki kodları kullanabilirsiniz

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

İşletim sistemi türünü kontrol ederek ve sonucu döndürerek her ikisini de tek bir işlevde birleştirebilirsiniz.


7

Bu standart kitaplık ile linux ve pencereler için de çalışıyor

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]

Sonuçlar:

['c', 'c', 'c', 'c', 'c', 'c', 'c']

6

İşte herhangi bir işletim sistemindeki herhangi bir işletim sistemi yolu ile çalışıyor gibi görünen normal regex çözümü.

Başka bir modüle gerek yoktur ve ön işleme gerek yoktur:

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

Güncelleme:

Yalnızca istiyorsanız potansiyel dosya mevcut (yani eğer /a/b/bir dir ve böyledir c:\windows\), için regex'i değiştirin: r'[^\\/]+(?![\\/])$'. "Regex meydan okudu" için, bu, bir tür eğik çizgi için pozitif ileriye doğru ileriye bakmayı negatif ileriye doğru bir ileriye çevirir ve adı geçen eğik çizgi ile biten yol adlarının yol adındaki son alt dizin yerine hiçbir şey döndürmemesine neden olur . Elbette, potansiyel dosya adının aslında bir dosyaya atıfta bulunduğunu ve bunun için kullanılması os.path.is_dir()ya os.path.is_file()da kullanılması gerekeceğini garanti etmez .

Bu şu şekilde eşleşecektir:

/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

Normal ifade burada test edilebilir .


re kullanıyorsunuz, neden os modülü değil?
Saurabh Chandra Patel

@SaurabhChandraPatel uzun zaman oldu. Doğru hatırlarsam, regex bu durumda çapraz platform çözümü olarak kullanılır. Örneğin bir Linux sunucusunda windows dosya adlarını işleyebilirsiniz.
Eric Duminil

5

Belki de sadece benim yeni bir çözüm önemli olmadan bazı yeni (geçici dosyaları oluşturmak için geçici dosya göz önünde bulundurun: D)

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

Değerleri almak abc.nameşöyle bir dize olacaktır: '/tmp/tmpks5oksk7' Böylece /bir boşluk ile değiştirin .replace("/", " ")ve sonra arayın split(). Bu bir liste döndürecek ve listenin son öğesini[-1]

Herhangi bir modülün ithal edilmesine gerek yoktur.


2
Dosya adı veya dizin bir boşluk içeriyorsa ne olur?
kriss

1
Doğrudan bölünme ("/") [- 1] ne olacak?
Nan

4

Daha önce çift ters eğik çizgiler görmedim, bunlar var mı? Python modülünün yerleşik özelliği bunlar osiçin başarısız olur. Tüm diğerleri, ayrıca sizin tarafınızdan verilen uyarı ile çalışır 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))

Bunlar çifte backslah değil. Tek ters eğik çizgilerdir ve kaçmaları gerekir.
Eric Duminil

3

Windows ayırıcı bir Unix dosya adında veya Windows Yolunda olabilir. Unix ayırıcısı yalnızca Unix yolunda bulunabilir. Unix ayırıcısının varlığı, Windows olmayan bir yolu gösterir.

Aşağıdakiler, OS'ye özgü ayırıcı tarafından sıyrılır (kesme iz ayırıcısı), daha sonra bölünür ve en sağdaki değeri döndürür. Yukarıdaki varsayımlara dayanarak çirkin ama basit. Varsayım yanlışsa, lütfen güncelleyin; bu yanıtı daha doğru koşullara uyacak şekilde güncelleyeceğim.

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

basit kod:

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
Ayrıca, bu mekanda nasıl formatlanacağına dair işaretçiler göndermekten çekinmeyin. Yarım düzine örnek kodunu almaya çalıştı.
dusc2don

1

pathlibTamlık uğruna, python 3.2+ için çözüm:

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

Bu hem Windows hem de Linux üzerinde çalışır.


1

Python 2 ve 3'te pathlib2 modülünü kullanarak :

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)

Kullanımı:

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

Test çantanızla:

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

Buradaki fikir, tüm yolları pathlib2platforma bağlı olarak farklı kod çözücülerle birleştirilmiş dahili temsile dönüştürmektir . Neyse ki, herhangi bir yolda çalışması gereken pathlib2genel bir kod çözücü içerir PurePath. Bu işe yaramazsa, kullanarak Windows yolunu tanımayı zorlayabilirsinizfromwinpath=True . Bu, giriş dizesini parçalara böler, sonuncusu aradığınız yaprak, dolayısıyla path2unix(t)[-1].

Bağımsız değişken nojoin=Falseise, yol geri birleştirilir, böylece çıktı yalnızca platformlar arasındaki alt yolları karşılaştırmak için yararlı olabilecek bir Unix biçimine dönüştürülen giriş dizesidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.