簡體   English   中英

跨平台拆分python中的路徑

[英]cross-platform splitting of path in python

我想要一些效果與此相同的東西:

>>> path = "/foo/bar/baz/file"
>>> path_split = path.rsplit('/')[1:]
>>> path_split
['foo', 'bar', 'baz', 'file']

但這也適用於Windows路徑。 我知道有一個os.path.split()但這不能做我想要的,我沒有看到任何事情。

Python 3.4引入了一個新的模塊pathlib pathlib.Path提供與文件系統相關的方法,而pathlib.PurePath完全獨立於文件系統運行:

>>> from pathlib import PurePath
>>> path = "/foo/bar/baz/file"
>>> path_split = PurePath(path).parts
>>> path_split
('\\', 'foo', 'bar', 'baz', 'file')

您可以在需要時顯式使用PosixPath和WindowsPath:

>>> from pathlib import PureWindowsPath, PurePosixPath
>>> PureWindowsPath(path).parts
('\\', 'foo', 'bar', 'baz', 'file')
>>> PurePosixPath(path).parts
('/', 'foo', 'bar', 'baz', 'file')

當然,它也適用於Windows路徑:

>>> wpath = r"C:\foo\bar\baz\file"
>>> PurePath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PureWindowsPath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PurePosixPath(wpath).parts
('C:\\foo\\bar\\baz\\file',)
>>>
>>> wpath = r"C:\foo/bar/baz/file"
>>> PurePath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PureWindowsPath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PurePosixPath(wpath).parts
('C:\\foo', 'bar', 'baz', 'file')

Huzzah for Python開發人員不斷改進語言!

OP指定“也適用於Windows路徑”。 Windows路徑有一些皺紋。

首先,Windows具有多個驅動器的概念,每個驅動器都有自己的當前工作目錄,而'c:foo''c:\\\\foo'通常不一樣。 因此,首先使用os.path.splitdrive()分離出任何驅動器指示符是一個非常好的主意。 然后可以通過drive + os.path.join(*other_pieces)正確地重新組裝路徑(如果需要drive + os.path.join(*other_pieces)

其次,Windows路徑可以包含斜杠或反斜杠或混合。 因此,在解析非規范化路徑時使用os.sep是沒有用的。

更普遍:

'foo''foo/'生成的結果不應該相同。

循環終止條件似乎最好表示為“os.path.split()將其輸入視為不可分割”。

這是一個建議的解決方案,包括測試,包括與@Spacedman解決方案的比較

import os.path

def os_path_split_asunder(path, debug=False):
    parts = []
    while True:
        newpath, tail = os.path.split(path)
        if debug: print repr(path), (newpath, tail)
        if newpath == path:
            assert not tail
            if path: parts.append(path)
            break
        parts.append(tail)
        path = newpath
    parts.reverse()
    return parts

def spacedman_parts(path):
    components = [] 
    while True:
        (path,tail) = os.path.split(path)
        if not tail:
            return components
        components.insert(0,tail)

if __name__ == "__main__":
    tests = [
        '',
        'foo',
        'foo/',
        'foo\\',
        '/foo',
        '\\foo',
        'foo/bar',
        '/',
        'c:',
        'c:/',
        'c:foo',
        'c:/foo',
        'c:/users/john/foo.txt',
        '/users/john/foo.txt',
        'foo/bar/baz/loop',
        'foo/bar/baz/',
        '//hostname/foo/bar.txt',
        ]
    for i, test in enumerate(tests):
        print "\nTest %d: %r" % (i, test)
        drive, path = os.path.splitdrive(test)
        print 'drive, path', repr(drive), repr(path)
        a = os_path_split_asunder(path)
        b = spacedman_parts(path)
        print "a ... %r" % a
        print "b ... %r" % b
        print a == b

這是輸出(Python 2.7.1,Windows 7 Pro):

Test 0: ''
drive, path '' ''
a ... []
b ... []
True

Test 1: 'foo'
drive, path '' 'foo'
a ... ['foo']
b ... ['foo']
True

Test 2: 'foo/'
drive, path '' 'foo/'
a ... ['foo', '']
b ... []
False

Test 3: 'foo\\'
drive, path '' 'foo\\'
a ... ['foo', '']
b ... []
False

Test 4: '/foo'
drive, path '' '/foo'
a ... ['/', 'foo']
b ... ['foo']
False

Test 5: '\\foo'
drive, path '' '\\foo'
a ... ['\\', 'foo']
b ... ['foo']
False

Test 6: 'foo/bar'
drive, path '' 'foo/bar'
a ... ['foo', 'bar']
b ... ['foo', 'bar']
True

Test 7: '/'
drive, path '' '/'
a ... ['/']
b ... []
False

Test 8: 'c:'
drive, path 'c:' ''
a ... []
b ... []
True

Test 9: 'c:/'
drive, path 'c:' '/'
a ... ['/']
b ... []
False

Test 10: 'c:foo'
drive, path 'c:' 'foo'
a ... ['foo']
b ... ['foo']
True

Test 11: 'c:/foo'
drive, path 'c:' '/foo'
a ... ['/', 'foo']
b ... ['foo']
False

Test 12: 'c:/users/john/foo.txt'
drive, path 'c:' '/users/john/foo.txt'
a ... ['/', 'users', 'john', 'foo.txt']
b ... ['users', 'john', 'foo.txt']
False

Test 13: '/users/john/foo.txt'
drive, path '' '/users/john/foo.txt'
a ... ['/', 'users', 'john', 'foo.txt']
b ... ['users', 'john', 'foo.txt']
False

Test 14: 'foo/bar/baz/loop'
drive, path '' 'foo/bar/baz/loop'
a ... ['foo', 'bar', 'baz', 'loop']
b ... ['foo', 'bar', 'baz', 'loop']
True

Test 15: 'foo/bar/baz/'
drive, path '' 'foo/bar/baz/'
a ... ['foo', 'bar', 'baz', '']
b ... []
False

Test 16: '//hostname/foo/bar.txt'
drive, path '' '//hostname/foo/bar.txt'
a ... ['//', 'hostname', 'foo', 'bar.txt']
b ... ['hostname', 'foo', 'bar.txt']
False

有人說“使用os.path.split ”。 不幸的是,這被刪除了,但這是正確的答案。

os.path.split這樣的(路徑)

將路徑名路徑拆分為一對(頭部,尾部),其中tail是最后一個路徑名組件,head是指向該路徑的所有內容。 尾部永遠不會有斜線; 如果path以斜線結尾,則tail將為空。 如果路徑中沒有斜杠,則head將為空。 如果path為空,則head和tail都為空。 除非是根(僅限一個或多個斜杠),否則會從頭部刪除尾部斜杠。 在所有情況下,join(head,tail)返回與path相同位置的路徑(但字符串可能不同)。

所以它不只是拆分目錄名和文件名。 您可以多次應用它以便攜式和正確的方式獲得完整路徑。 代碼示例:

dirname = path
path_split = []
while True:
    dirname, leaf = split(dirname)
    if leaf:
        path_split = [leaf] + path_split #Adds one element, at the beginning of the list
    else:
        #Uncomment the following line to have also the drive, in the format "Z:\"
        #path_split = [dirname] + path_split 
        break

如果答案取消刪除,請將原作者歸功於原創作者。

使用os.path提供的功能,例如

os.path.split(path)

就像在別處寫的一樣,你可以多次調用它來分割更長的路徑。

這是迭代使用os.path.split的方法的顯式實現; 使用與接受的答案略有不同的循環終止條件。

def splitpath(path):
    parts=[]
    (path, tail)=os.path.split( path)
    while path and tail:
         parts.append( tail)
         (path,tail)=os.path.split(path)
    parts.append( os.path.join(path,tail) )
    return map( os.path.normpath, parts)[::-1]

這應該滿足os.path.join( *splitpath(path) )path ,因為它們都指示相同的文件/目錄。

在linux中測試:

In [51]: current='/home/dave/src/python'

In [52]: splitpath(current)
Out[52]: ['/', 'home', 'dave', 'src', 'python'] 

In [53]: splitpath(current[1:])
Out[53]: ['.', 'dave', 'src', 'python']

In [54]: splitpath( os.path.join(current, 'module.py'))
Out[54]: ['/', 'home', 'dave', 'src', 'python', 'module.py']

In [55]: splitpath( os.path.join(current[1:], 'module.py'))
Out[55]: ['.', 'dave', 'src', 'python', 'module.py']

我手動檢查了一些DOS路徑,使用通過用ntpath模塊替換os.path ,看起來沒關系,但我不太熟悉DOS路徑的來龍去脈。

使用os.path中提供的功能,例如

os.path.split(path)

(這個答案是由其他人進行的,並且是神秘且錯誤地刪除的,因為它是一個有效的答案;如果你想將路徑的每個部分分開,你可以多次調用它,每次調用都會將一個組件拉到最后。)

再試一次使用maxplit選項,它是os.path.split()的替代品

def pathsplit(pathstr, maxsplit=1):
    """split relative path into list"""
    path = [pathstr]
    while True:
        oldpath = path[:]
        path[:1] = list(os.path.split(path[0]))
        if path[0] == '':
            path = path[1:]
        elif path[1] == '':
            path = path[:1] + path[2:]
        if path == oldpath:
            return path
        if maxsplit is not None and len(path) > maxsplit:
            return path

所以繼續使用os.path.split,直到達到你想要的效果。 這是一個使用無限循環的丑陋實現:

import os.path
def parts(path):
    components = [] 
    while True:
        (path,tail) = os.path.split(path)
        if tail == "":
            components.reverse()
            return components
        components.append(tail)

堅持在parts.py,導入部分和瞧:

>>> parts.parts("foo/bar/baz/loop")
['foo', 'bar', 'baz', 'loop']

可能是使用生成器或遞歸的更好的實現...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM