programing

두 절대 경로를 비교하여 상대 경로 가져오기

telebox 2023. 6. 21. 22:30
반응형

두 절대 경로를 비교하여 상대 경로 가져오기

예를 들어, 저에게는 두 가지 절대적인 길이 있습니다.경로 중 하나가 가리키는 위치가 다른 경로의 후손인지 확인해야 합니다.사실이라면 조상으로부터 후손의 상대적인 경로를 알아내야 합니다.파이썬에서 이를 구현하는 좋은 방법은 무엇입니까?제가 혜택을 받을 수 있는 도서관이 있나요?

os.path.commonprefix()os.path.relpath()는 친구입니다.

>>> print os.path.commonprefix(['/usr/var/log', '/usr/var/security'])
'/usr/var'
>>> print os.path.commonprefix(['/tmp', '/usr/var'])  # No common prefix: the root is the common prefix
'/'

따라서 공통 접두사가 경로 중 하나인지, 즉 경로 중 하나가 공통 조상인지 테스트할 수 있습니다.

paths = […, …, …]
common_prefix = os.path.commonprefix(list_of_paths)
if common_prefix in paths:
    …

그런 다음 상대 경로를 찾을 수 있습니다.

relative_paths = [os.path.relpath(path, common_prefix) for path in paths]

이 방법을 사용하여 두 개 이상의 경로를 처리하고 모든 경로가 두 경로 중 하나 아래에 있는지 테스트할 수도 있습니다.

PS: 경로의 모양에 따라 먼저 정규화를 수행할 수 있습니다(항상 '/'로 끝나는지 여부를 알 수 없거나 일부 경로가 상대적인 경우에 유용합니다).관련 함수에는 os.path.abspath() os.path.normpath()포함됩니다.

PPS: Peter Briggs가 코멘트에서 언급했듯이, 위에서 설명한 간단한 접근법은 실패할 수 있습니다.

>>> os.path.commonprefix(['/usr/var', '/usr/var2/log'])
'/usr/var'

그럼에도 불구하고./usr/var경로의 일반 접두사가 아닙니다.호출하기 전에 모든 경로가 '/'로 끝나도록 강제하는 중commonprefix()이 (특정) 문제를 해결합니다.

PPPS: 블루노트10에서 언급한 것처럼 슬래시를 추가한다고 해서 일반적인 문제가 해결되지는 않습니다.다음은 그의 후속 질문입니다.Python의 os.path.common 접두사의 오류를 피하는 방법은 무엇입니까?

PPPPS: Python 3.4부터, 우리는 더 깔끔한 경로 조작 환경을 제공하는 모듈인 pathlib을 가지고 있습니다.경로 집합의 공통 접두사는 각 경로의 모든 접두사를 가져와서(와) 이러한 모든 부모 집합의 교차점을 취하고 가장 긴 공통 접두사를 선택함으로써 얻을 수 있다고 생각합니다.

PPPPS: Python 3.5는 유효한 경로를 반환하는 이 질문에 적절한 솔루션을 도입했습니다.

os.path.relpath:

현재 디렉터리 또는 선택적 시작점에서 상대 파일 경로를 경로로 반환합니다.

>>> from os.path import relpath
>>> relpath('/usr/var/log/', '/usr/var')
'log'
>>> relpath('/usr/var/log/', '/usr/var/sad/')
'../log'

따라서 상대 경로가 다음으로 시작하는 경우'..'두 번째 경로가 첫 번째 경로의 하위가 아님을 의미합니다.

Python3에서는 다음을 사용할 수 있습니다.

Python 3.5.1 (default, Jan 22 2016, 08:54:32)
>>> from pathlib import Path

>>> Path('/usr/var/log').relative_to('/usr/var/log/')
PosixPath('.')

>>> Path('/usr/var/log').relative_to('/usr/var/')
PosixPath('log')

>>> Path('/usr/var/log').relative_to('/etc/')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/pathlib.py", line 851, in relative_to
    .format(str(self), str(formatted)))
ValueError: '/usr/var/log' does not start with '/etc'

Python 3에서 pathlib을 사용한 jme의 제안에 대한 글입니다.

from pathlib import Path
parent = Path(r'/a/b')
son = Path(r'/a/b/c/d')            
​
if parent in son.parents or parent==son:
    print(son.relative_to(parent)) # returns Path object equivalent to 'c/d'

또 다른 옵션은

>>> print os.path.relpath('/usr/var/log/', '/usr/var')
log

dep이 없는 Pure Python2:

def relpath(cwd, path):
    """Create a relative path for path from cwd, if possible"""
    if sys.platform == "win32":
        cwd = cwd.lower()
        path = path.lower()
    _cwd = os.path.abspath(cwd).split(os.path.sep)
    _path = os.path.abspath(path).split(os.path.sep)
    eq_until_pos = None
    for i in xrange(min(len(_cwd), len(_path))):
        if _cwd[i] == _path[i]:
            eq_until_pos = i
        else:
            break
    if eq_until_pos is None:
        return path
    newpath = [".." for i in xrange(len(_cwd[eq_until_pos+1:]))]
    newpath.extend(_path[eq_until_pos+1:])
    return os.path.join(*newpath) if newpath else "."

편집 : Python3에서 가장 좋은 방법은 jme의 답변을 참고하세요.

pathlib을 사용하면 다음과 같은 해결책이 있습니다.

우리가 확인하고 싶다고 가정해 보겠습니다.son는 의후니다의 입니다.parent 다 그리고 둘다다 둘▁and입니다.Path물건들.다음을 통해 경로에 있는 부품의 목록을 얻을 수 있습니다.list(parent.parts)그런 다음 아들의 시작이 부모의 세그먼트 목록과 동일한지 확인합니다.

>>> lparent = list(parent.parts)
>>> lson = list(son.parts)
>>> if lson[:len(lparent)] == lparent:
>>> ... #parent is a parent of son :)

나머지 부분을 받고 싶으면 그냥 하면 됩니다.

>>> ''.join(lson[len(lparent):])

문자열이지만 다른 경로 개체의 생성자로 사용할 수도 있습니다.

언급URL : https://stackoverflow.com/questions/7287996/get-relative-path-from-comparing-two-absolute-paths

반응형