파이썬 : 두 절대 경로를 비교하여 상대 경로 얻기
두 가지 절대 경로가 있다고 가정 해보십시오. 경로 중 하나가 참조하는 위치가 다른 경로의 자손인지 확인해야합니다. 사실이라면 조상으로부터 후손의 상대 경로를 찾아야합니다. 파이썬에서 이것을 구현하는 좋은 방법은 무엇입니까? 혜택을받을 수있는 라이브러리가 있습니까?
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]
이 방법을 사용하여 둘 이상의 경로를 처리하고 모든 경로가 모두 하나 아래에 있는지 테스트 할 수 있습니다.
추신 : 경로의 모양에 따라 먼저 정규화를 수행하려고 할 수 있습니다 (이것은 항상 '/'로 끝나는 지 아닌지 또는 일부 경로가 상대적인지 모르는 상황에서 유용합니다). 관련 함수에는 os.path.abspath () 및 os.path.normpath ()가 있습니다.
PPS : Peter Briggs가 의견에서 언급했듯이 위에서 설명한 간단한 접근 방식은 실패 할 수 있습니다.
>>> os.path.commonprefix(['/usr/var', '/usr/var2/log'])
'/usr/var'
비록 경로의 일반적인 접두사 /usr/var
가 아닙니다 . 호출하기 전에 모든 경로를 '/'로 commonprefix()
끝내면이 (특정) 문제가 해결됩니다.
PPPS : bluenote10에서 언급했듯이 슬래시를 추가해도 일반적인 문제는 해결되지 않습니다. 그의 후속 질문은 다음과 같습니다. Python의 os.path.commonprefix의 오류를 피하는 방법?
PPPPS : Python 3.4부터는 더 안전한 경로 조작 환경을 제공하는 모듈 인 pathlib가 있습니다. 경로 세트의 공통 접두사는 각 경로의 모든 접두사 ( PurePath.parents()
)를 가져 와서이 모든 부모 세트의 교차점을 취하고 가장 긴 공통 접두사를 선택하여 얻을 수 있다고 생각 합니다.
PPPPPS : Python 3.5는이 질문에 대한 올바른 해결책을 제시했습니다 os.path.commonpath()
. 유효한 경로를 반환합니다.
현재 디렉토리 또는 선택적 시작점에서 상대 경로를 경로로 리턴하십시오.
>>> from os.path import relpath
>>> relpath('/usr/var/log/', '/usr/var')
'log'
>>> relpath('/usr/var/log/', '/usr/var/sad/')
'../log'
따라서 상대 경로로 시작 '..'
하면 두 번째 경로가 첫 번째 경로의 후손이 아님을 의미합니다.
Python3에서는 다음을 사용할 수 있습니다 PurePath.relative_to
.
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'
다른 옵션은
>>> print os.path.relpath('/usr/var/log/', '/usr/var')
log
순수 Python2 (dep 없음) :
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 "."
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:
print(son.relative_to(parent)) # returns Path object equivalent to 'c/d'
편집 : Python3을 사용하는 가장 좋은 방법은 jme의 답변을 참조하십시오.
pathlib를 사용하면 다음과 같은 솔루션이 있습니다.
son
의 자손 인지 확인 parent
하고 둘 다 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):])
문자열이지만 다른 Path 객체의 생성자로 사용할 수도 있습니다.
'IT박스' 카테고리의 다른 글
Django 앱의 이름을 변경하는 방법은 무엇입니까? (0) | 2020.07.04 |
---|---|
너무 오래 걸리는 SQL Server Broker 사용 (0) | 2020.07.04 |
하스켈에서 "리프팅"이란 무엇입니까? (0) | 2020.07.03 |
GNU Make에서 CPPFLAGS와 CXXFLAGS의 차이점 (0) | 2020.07.03 |
지원 라이브러리에서 PreferenceActivity로 작업 표시 줄을 추가하는 방법은 무엇입니까? (0) | 2020.07.03 |