Dos yolu Python'daki bileşenlerine nasıl bölünür


154

Bir dos yolu temsil eden bir dize değişkeni örneğin:

var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

Bu dize bölmek istiyorum:

[ "d", "stuff", "morestuff", "furtherdown", "THEFILE.txt" ]

Kullanmayı denedim split()ve replace()sadece ilk ters eğik çizgiyi işliyorlar veya dizeye onaltılık sayılar ekliyorlar.

Ayrıştırmak için bir şekilde bu dize değişkeni ham bir dizeye dönüştürmek gerekiyor.

Bunu yapmanın en iyi yolu nedir?

Ayrıca varayrıştırmaya çalıştığım yolun içeriği aslında bir komut satırı sorgusunun dönüş değeri olduğunu eklemeliyim . Kendi oluşturduğum yol verisi değil. Bir dosyada saklanır ve komut satırı aracı ters eğik çizgilerden kaçmayacaktır.


6
Bu yanıtları incelerken, bunun os.path.splitsizin için işe yaramadığını unutmayın, çünkü o dizeden düzgün şekilde kaçmıyorsunuz.
Jed Smith

Dizeden kaçmanız veya rawstring kullanmanız gerekir: yanlış yorumlanmayı r"d:\stuff\morestuff\furtherdown\THEFILE.txt"önlemek için \s.
smci

Yanıtlar:


165

Kendi yolunu kuran ve yanlış anlayan insanlar tarafından birçok kez ısırıldım. Alanlar, eğik çizgiler, ters eğik çizgiler, iki nokta üst üste - karışıklık olasılıkları sonsuz değildir, ancak yine de hatalar kolayca yapılır. Bu yüzden kullanımı için bir çöpçüyüm os.pathve bu temelde tavsiye ederim.

(Ancak, erdemin yolu en kolay alınan yol değildir ve bunu bulurken birçok insan doğrudan lanetlemeye kaygan bir yol izlemeye caziptir. Bir güne kadar her şey parçalanana kadar fark etmezler ve , büyük olasılıkla, başka biri - neden her şeyin yanlış gittiğini bulmalı ve birisi eğik çizgileri ve ters eğik çizgileri karıştıran bir dosya adı yaptı - ve bazı kişi cevabın "bunu yapmamak" olduğunu öne sürüyor. Bu insanlardan herhangi biri olmayın. Eğik çizgileri ve ters eğik çizgileri karıştıranlar hariç - isterseniz onlar olabilirsiniz.)

Sürücü ve yol + dosyasını şu şekilde alabilirsiniz:

drive, path_and_file = os.path.splitdrive(path)

Yolu ve dosyayı alın:

path, file = os.path.split(path_and_file)

Bireysel klasör adlarını almak özellikle uygun değildir, ancak daha sonra gerçekten iyi çalışan bir şey bulma zevkini artıran dürüst orta halli rahatsızlıktır:

folders = []
while 1:
    path, folder = os.path.split(path)

    if folder != "":
        folders.append(folder)
    else:
        if path != "":
            folders.append(path)

        break

folders.reverse()

( Yol "\"başlangıçta mutlaksa başlangıcında bir belirir folders. Bunu istemediyseniz biraz kod kaybedebilirsiniz.)


@brone - Ters eğik çizgiden kaçmaktan endişelenmek yerine bu çözümü kullanmayı tercih ederim. Teşekkürler!
BeeBand

1
Yanlış kanıtlanmış olmaktan mutluluk duyarım ama bana böyle bir yol kullanılırsa önerilen çözüm işe yaramaz gibi görünüyor (C: \ usr \ rs0 \ my0 \ in111102.log "(ilk girdi bir ham dize olmadığı sürece) )?
shearichard

1
Bu iki satırı en başa eklemek istediğinizi başarmak için, OSX'de yalnızca "/ path / to / my / folder /" gibi bir dizin içeriyorsa, bunun düzgün bir şekilde bölünmeyeceği anlaşılıyor: if path.endswith("/"):ve path = path[:-1].
Kevin London

1
@Tompa tarafından çözümü tercih ediyorum
jaycode 26:15

1
Ben hemfikir jaycode : Tompa 'ın çözüm olduğunu kanonik yaklaşımı ve kabul cevap olması gerekirdi. Bu aşırı karmaşık, verimsiz ve hataya açık alternatif, üretim kodunda başarılı olamıyor. Orada hiçbir girişimi (... ve tabii ki, başarısız) basit dize bölme yalnızca ile başarılı olduğunda iteratif pathnames ayrı ayrıştırmak için makul bir neden kod tek satır.
Cecil Curry

287

yapardım

import os
path = os.path.normpath(path)
path.split(os.sep)

İlk olarak yol dizesini işletim sistemi için uygun bir dizeye normalleştirin. O zaman os.sepstring fonksiyonunda bir sınırlayıcı olarak kullanmak güvenli olmalıdır.


25
Gerçek cevap: ortaya çıktı . Kanonik çözüm elbette en basitidir. Seyretmek! Çünkü zarif ve sürekli ve uçsuz bucaksız kenar kasaları yok.
Cecil Curry

20
Tek hatlı olarak,os.path.normpath(a_path).split(os.path.sep)
Daniel Farrell

2
Bu path = root için işe yaramıyor gibi görünüyor. Bu durumda, path.split öğesinin sonucu ['', ''] olur. Aslında, bu split () çözümü boş dize adıyla (en uygun eğik çizgiyle değiştirilebilir) en soldaki dizini verir. Temel sorun, tek bir eğik çizginin (işletim sistemine bağlı olarak ileri veya geri) kök dizininin adı , yolun başka bir yerinde ise bir ayırıcı olmasıdır .
gwideman

2
O zaman bir lstrip ile daha iyi çalışır mı? os.path.normpath(path).lstrip(os.path.sep).split(os.path.sep)
Vidar

1
@ user60561 Bunun nedeni Linux'ta ters eğik çizginin dosya adlarında izin verilen bir karakter olmasına karşın, Windows'ta eğik çizgi olmaması. Bu nedenle Windows'ta normpatheğik çizgiyi ayırıcı olarak tanıyacaktır. Linux'ta, normpathadlı bir dizinin \1\2ve içinde bir dosya veya dizinin bulunduğunu varsayarsınız 3.
Vojislav Stojkovic

81

En Pythonic yaklaşımını (IMHO) kullanabilirsiniz:

import os

your_path = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
path_list = your_path.split(os.sep)
print path_list

Hangi size verecek:

['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Buradaki ipucu , sistemi bağımsız kıldığı için veya os.sepyerine kullanmaktır .'\\''/'

Sürücü harfinden iki nokta üst üste işaretini kaldırmak için (bunu neden yapmak istediğinizin herhangi bir nedenini görmeme rağmen), şunu yazabilirsiniz:

path_list[0] = path_list[0][0]

22
Bu işe yarıyor some times. Diğer zamanlarda (en azından pencerelerde) benzer yollar bulacaksınız folder\folder2\folder3/file.txt. İlk önce yolu normalleştirmek (os.path.normpath) ve daha sonra bölmek daha iyidir.
vikki

7
Bu cevap neredeyse oradaydı. Gibi vikki anlaşılacağı, dize bölme büyü önce normalleştirmek yol adlarının için başarısızlık sıradan kenardan davalarda mahkum (örneğin /foo//bar). Bkz Tompa 'ın cevabı daha sağlam bir çözüm.
Cecil Curry

62

Python> = 3.4'te bu çok daha basit hale geldi. Artık pathlib.Path.partsbir yolun tüm parçalarını almak için kullanabilirsiniz .

Misal:

>>> from pathlib import Path
>>> Path('C:/path/to/file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> Path(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')

Python 3'ün Windows kurulumunda bu, Windows yollarıyla çalıştığınızı varsayar ve * nix'de posix yollarıyla çalıştığınızı varsayar. Genellikle istediğiniz budur, ancak değilse sınıfları pathlib.PurePosixPathveya pathlib.PureWindowsPathgerektiği gibi kullanabilirsiniz :

>>> from pathlib import PurePosixPath, PureWindowsPath
>>> PurePosixPath('/path/to/file.txt').parts
('/', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'\\host\share\path\to\file.txt').parts
('\\\\host\\share\\', 'path', 'to', 'file.txt')

Düzenleme: Ayrıca python 2 için bir backport var: pathlib2


3
Path.parts her zaman istediğim şeydi, ama bugüne kadar var olduğunu asla bilmiyordum.
JamEnergy

bu neden güzel bir yerli python fonksiyonunun etrafına sarılmamış?
Eduardo Pignatelli

2
Cevap bu!
nayriz

11

Buradaki sorun, dizeyi nasıl oluşturduğunuzla başlar.

a = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

Bu şekilde yapılması, Python özel durum bu çalışıyor \s, \m, \f, ve \T. Sizin durumunuzda, \fdiğer ters eğik çizgiler doğru şekilde işlenirken form besleme (0x0C) olarak işlem görüyor. Yapmanız gerekenlerden biri:

b = "d:\\stuff\\morestuff\\furtherdown\\THEFILE.txt"      # doubled backslashes
c = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"         # raw string, no doubling necessary

Sonra bunlardan herhangi birini böldüğünüzde, istediğiniz sonucu elde edersiniz.


@ W. Craig Trader - teşekkürler, ama bu yol kendimi ürettiğim bir yol değil - bana başka bir programdan geri geliyor ve bu verileri bir değişkende saklamak zorundayım. Bir değişkende saklanan verileri "ham metin" dönüştürmek nasıl emin değilim.
BeeBand

"Ham metin" diye bir şey yoktur ... onu kaynakta nasıl temsil edersiniz. Dizeye r "" başlığını ekleyin veya .replace ('\\', '/') 'den geçirin
Marco Mariani

@BeeBand, verileri diğer programdan nasıl geri alıyorsunuz? Bir dosyadan, bir borudan, bir soketten mi okuyorsunuz? Eğer öyleyse, o zaman fantezi bir şey yapmanıza gerek yoktur; ters eğik çizgileri ikiye katlamanın veya ham dizeleri kullanmanın tek nedeni, dize sabitlerini Python koduna yerleştirmektir. Öte yandan, diğer program iki katına ters eğik çizgi oluşturuyorsa, yolunuzu bölmeden önce bunu temizlemek istersiniz.
Craig Trader

@ W. Craig Trader - başka bir program tarafından yazılmış olan bir dosyadan okuyorum. Bir sebepten dolayı çalışamadım split()veya replace()çalışamadım - onaltılık değerler almaya devam ettim. Haklısın, sanırım yanlış ipi ham ip fikri ile havlıyorum - sanırım split()yanlış kullanıyordum . Çünkü bu çözümlerden bazılarını kullanarak denedim split()ve şimdi benim için çalışıyorlar.
BeeBand

10

Biraz daha özlü bir çözüm için aşağıdakileri göz önünde bulundurun:

def split_path(p):
    a,b = os.path.split(p)
    return (split_path(a) if len(a) and len(b) else []) + [b]

Bu, bu soruna en sevdiğim çözüm. Çok hoş.
Will Moore

1
Yol biterse bu çalışmaz /. Ayrıca, /
yolunuzla

4

Aslında buna gerçek bir cevap veremem (buraya kendimi bulmayı umarak geldim), ama bana göre farklı yaklaşımların sayısı ve belirtilen tüm uyarılar Python'un os.path modülünün umutsuzca buna ihtiyaç duyduğu en kesin göstergedir. yerleşik bir işlev olarak.


4

Bir jeneratör ile fonksiyonel yol .

def split(path):
    (drive, head) = os.path.splitdrive(path)
    while (head != os.sep):
        (head, tail) = os.path.split(head)
        yield tail

Eylemde:

>>> print([x for x in split(os.path.normpath('/path/to/filename'))])
['filename', 'to', 'path']

3

Benim için çalışıyor:

>>> a=r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
>>> a.split("\\")
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Tabii ki, kolonu ilk bileşenden çıkarmanız gerekebilir, ancak bunu korumak yolu yeniden birleştirmeyi mümkün kılar.

rDeğiştirici işaretleri "ham" olarak düz dize; gömülü ters eğik çizgilerin nasıl iki katına çıkmadığına dikkat edin.


@ unwind - dizenizin rönünde, bu ne anlama geliyor?
BeeBand

2
r ham dize anlamına gelir - \ karakterlerden otomatik olarak kaçar . Her yol yaparken kullanışlıdır.
Wayne Werner

1
@BeeBand: önemsemenize gerek yok; r "" kodun derlenmesi / ayrıştırılması sırasında önemli olan bir şeydir, ayrıştırıldıktan sonra dizenin bir özelliği haline gelen bir şey değildir. Bunun anlamı "işte bir dize değişmezidir, ancak ters eğik çizgileri ters eğik çizgi olmaktan başka bir anlamı olduğu olarak yorumlamayın".
gevşeyin

3
Ben eksi iyi sert kodlama yerine a.split (os.sep) kullanarak daha belirsiz yapmak bahsetmek yararlı olabilir düşünüyorum?
Tim McJilton

4
Açıklamak için bir şansı kaçırdığınız için sizi aşağılamak zorundayım os.path.splitve os.pathsepher ikisinin de yazdıklarınızdan çok daha taşınabilir olduğunu düşünüyoruz. Şimdi OP için önemli olmayabilir, ancak platformları hareket ettirmesi gereken bir şey yazdığında olacaktır.
Jed Smith

3

Hakkında şeyler mypath.split("\\")daha iyi ifade edilecektir mypath.split(os.sep). sepplatformunuz için yol ayırıcıdır (örneğin, \Windows /için, Unix için, vb.) ve Python derlemesi hangisini kullanacağınızı bilir. Eğer kullanırsanız sep, sonra kod platformu agnostik olacaktır.


1
Veya os.path.split. Dikkatli olmak istiyorsun os.pathsep, çünkü :OS X'teki Python sürümümde (ve os.path.splitdüzgün şekilde işliyor /).
Jed Smith

4
Yani os.sep, değil os.pathsep. os.sepDokümanlardaki bilgeliği takip edin : Bunun yol adlarını ayrıştırmak veya birleştirmek için yeterli olmadığını bilmenin os.path.split () ve os.path.join () yöntemlerini kullanın.
Jon-Eric

1

re.split (), string.split () öğesinden biraz daha fazla yardımcı olabilir

import re    
var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"
re.split( r'[\\/]', var )
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Linux ve Mac yollarını da desteklemek istiyorsanız, filtre ekleyin (Yok, sonuç), böylece yolları '/' veya '//' ile başladığı için istenmeyen '' öğesini split () 'den kaldıracaktır. örneğin '// mount / ...' veya '/ var / tmp /'

import re    
var = "/var/stuff/morestuff/furtherdown/THEFILE.txt"
result = re.split( r'[\\/]', var )
filter( None, result )
['var', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

1

Dizeyi özyinelemeli os.path.splitolarak

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [p]

Bunu bazı yol dizelerine karşı test etmek ve yolu yeniden birleştirmek os.path.join

>>> for path in [
...         r'd:\stuff\morestuff\furtherdown\THEFILE.txt',
...         '/path/to/file.txt',
...         'relative/path/to/file.txt',
...         r'C:\path\to\file.txt',
...         r'\\host\share\path\to\file.txt',
...     ]:
...     print parts(path), os.path.join(*parts(path))
... 
['d:\\', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt'] d:\stuff\morestuff\furtherdown\THEFILE.txt
['/', 'path', 'to', 'file.txt'] /path\to\file.txt
['', 'relative', 'path', 'to', 'file.txt'] relative\path\to\file.txt
['C:\\', 'path', 'to', 'file.txt'] C:\path\to\file.txt
['\\\\', 'host', 'share', 'path', 'to', 'file.txt'] \\host\share\path\to\file.txt

Listenin ilk öğesine, sürücü harfleriyle, UNC yollarıyla ve mutlak ve göreli yollarla nasıl başa çıkmak istediğinize bağlı olarak farklı davranılması gerekebilir. Geçen değiştirme [p]için [os.path.splitdrive(p)]bölerek kuvvetlerin bir başlığın içine sürücü harfini ve dizin kök out sorunu.

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [os.path.splitdrive(p)]

[('d:', '\\'), 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
[('', '/'), 'path', 'to', 'file.txt']
[('', ''), 'relative', 'path', 'to', 'file.txt']
[('C:', '\\'), 'path', 'to', 'file.txt']
[('', '\\\\'), 'host', 'share', 'path', 'to', 'file.txt']

Edit: Bu cevap yukarıda user1556435 tarafından verilene çok benzer olduğunu fark ettim . Yolun sürücü bileşeninin işlenmesi farklı olduğundan cevabımı bırakıyorum.


0

Diğerleri açıkladığı gibi - sorununuz \dize değişmez / sabit dize kaçış karakteri kullanmaktan kaynaklandı . OTOH, bu dosya yolu dizesini başka bir kaynaktan alsaydı (dosyadan, konsoldan veya os işleviyle döndürülürse) - '\\' veya r '\' üzerinde bölme sorunu olmazdı.

Kullanmak istediğiniz takdirde Ve diğerleri önerdi tıpkı \programı sabit içinde, bunu çoğaltmak ya zorunda \\veya tüm edebi önek olarak zorundadır rşöyle, r'lite\ral'ya r"lite\ral"böyle dönüştürme ayrıştırıcı önlemek \ve rCR için (satırbaşı) karakterini.

Yine de bir yol daha var - \kodunuzda ters eğik çizgi yol adları kullanmayın ! Geçen yüzyıldan beri Windows, dizin ayırıcı olarak eğik çizgi kullanan yol adlarını tanır ve iyi çalışır /! Bir şekilde pek çok insan bunu bilmiyor .. ama işe yarıyor:

>>> var = "d:/stuff/morestuff/furtherdown/THEFILE.txt"
>>> var.split('/')
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Bu arada, kodunuzun Unix, Windows ve Mac üzerinde çalışmasını sağlarsınız ... çünkü /modülün önceden tanımlanmış sabitlerini kullanmak istemeseniz bile hepsi dizin ayırıcı olarak kullanılır os.


Ne yazık ki veriler bana python betiğimden çalıştırdığım başka bir programdan döndürülüyor. '\' Veya '/' kullanıp kullanamayacağım üzerinde hiçbir denetimim yok - bunu belirleyen üçüncü taraf programdır (muhtemelen platform bazında).
BeeBand

@BeeBand: Ah, o zaman dizeyi programınızda değişmez olarak verdiğinizde test sırasında yaşadığınız problemi yaşamayacaksınız. Veya yolu aldıktan sonra aşağıdaki kötü hack yapabilirsiniz: var = var.replace('\\','/')- \ ile / değiştirin ve sadece ileri eğik çizgilerle çalışmaya devam edin :)
Nas Banov

bu gerçekten kötü bir hack: o)
BeeBand

@BeeBand: bu yüzden uyardım. Bir şeyin kötü olduğunu söylediğimde, mutlaka kullanılmaması gerektiği anlamına gelmez - ama neden onu kullandıklarının ve istenmeyen sonuçların uyarısının çok fazla farkında olması gerekir . Bu durumda, çok olası olmayan bir sonuç, bu Unix dosya sisteminde `` dosya veya dizin adında kullanım (gerçekten zor ama mümkün) '' ile kullanılırsa - bu kodun 'kırılacağı'
Nas Banov

0

filedata.txtİçeriğe sahip bir dosyanız olduğunu varsayalım :

d:\stuff\morestuff\furtherdown\THEFILE.txt
d:\otherstuff\something\otherfile.txt

Dosya yollarını okuyabilir ve bölebilirsiniz:

>>> for i in open("filedata.txt").readlines():
...     print i.strip().split("\\")
... 
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
['d:', 'otherstuff', 'something', 'otherfile.txt']

bu gerçekten işe yarıyor, teşekkürler! Ama brone'un çözümünü seçtim çünkü ters eğik çizgiden kaçmak için endişelenmemeyi tercih ediyorum.
BeeBand

9
Dosya sistemine bağımlı olduğu için pitonik değil.
jb.

0

Aşağıdakileri, os.path.basename işlevini kullandığından, döndürülen listeye eğik çizgi eklemediği için kullanıyorum. Ayrıca herhangi bir platformun eğik çizgileri ile çalışır: örn. Pencerenin \\ veya unix's /. Dahası, pencerelerin sunucu yolları için kullandığı \\\\ ifadesini eklemez :)

def SplitPath( split_path ):
    pathSplit_lst   = []
    while os.path.basename(split_path):
        pathSplit_lst.append( os.path.basename(split_path) )
        split_path = os.path.dirname(split_path)
    pathSplit_lst.reverse()
    return pathSplit_lst

Yani '\\\\ sunucu \\ klasör1 \\ klasör2 \\ klasör3 \\ klasör4'

anladın

[ 'Sunucu', 'Folder1'ı', 'klasörün2', 'Folder3'ı', 'Klasör4']


1
Bu, sonucunuzu iletmenin os.path.join()orijinal dizeyi döndürmesi gereken değişkeni izlemez . Örnek girişiniz için doğru çıktı olduğunu söyleyebilirim [r'\\','server','folder1','folder2','folder3','folder4']. Yani ne os.path.split()yapar.
Jon-Eric

0

Aslında bu soruya tam olarak cevap verip vermediğinden emin değilim, ancak bir yığını tutan, os.path tabanlı manipülasyonlara yapışan ve öğelerin listesini / yığınını döndüren bu küçük işlevi yazarken eğlenceli zaman geçirdim.

  9 def components(path):
 10     ret = []
 11     while len(path) > 0:
 12         path, crust = split(path)
 13         ret.insert(0, crust)
 14
 15     return ret
 16

0

Aşağıdaki kod satırı işleyebilir:

  1. C: / yol / yolu
  2. C: // yolu // yolu
  3. C: \ yol \ yol
  4. C: \ yol \ yol

path = re.split (r '[/// \]', yol)


0

Eğlence için bir özyinelemeli.

En zarif cevap değil, her yerde çalışmalı:

import os

def split_path(path):
    head = os.path.dirname(path)
    tail = os.path.basename(path)
    if head == os.path.dirname(head):
        return [tail]
    return split_path(head) + [tail]

gerçekten üzgünüm. Soruyu dikkatlice okumalıydım ... 'dos' yolu.
DuGNu

-1

kullanım ntpath.split()


os.path.split () kullandığımda, alıyorum, ( d:\\stuff, morestuff\x0curtherdown\thefile.mux)
BeeBand

BeeBand'ın işaret ettiği gibi, os.path.split () gerçekten istenen şeyi yapmaz.
gevşeyin

üzgünüm os.path sadece os bağlı olarak çalıştığını fark ettim. ntpath dos yollarını ayrıştırır.
deft_code

ntpath ile bile hala d:\\stuff, morestuff\x0curtherdown\thefile.mux
alıyorum

2
@BeeBand: dizenizden kaçma ile ilgili sorunlarınız. '\x0c'form besleme karakteri. Form besleme karakterini oluşturmanın yolu '\ f'dir. Gerçekten '\ f' değişmez dizesini istiyorsanız iki seçeneğiniz vardır: '\\f'veya r'\f'.
deft_code
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.