पायथन: दो पूर्ण पथों की तुलना करने से सापेक्ष पथ प्राप्त करें


143

कहते हैं, मेरे पास दो पूर्ण मार्ग हैं। मुझे यह जांचने की आवश्यकता है कि क्या किसी एक पथ से संदर्भित स्थान दूसरे का वंशज है। अगर सच है, तो मुझे पूर्वज से वंश के रिश्तेदार मार्ग का पता लगाने की आवश्यकता है। पायथन में इसे लागू करने का एक अच्छा तरीका क्या है? कोई भी पुस्तकालय जिससे मैं लाभ उठा सकूं?

जवाबों:


167

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 : जैसा कि पीटर ब्रिग्स ने टिप्पणियों में उल्लेख किया है, ऊपर वर्णित सरल दृष्टिकोण विफल हो सकता है:

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

भले ही /usr/varहै नहीं रास्तों में से एक आम उपसर्ग। commonprefix()इस (विशिष्ट) समस्या को हल करने से पहले सभी रास्तों को '/' के साथ समाप्त करने के लिए मजबूर करना ।

PPPS : जैसा कि bluenote10 ने उल्लेख किया है, स्लैश जोड़ने से सामान्य समस्या हल नहीं होती है। यहाँ उसका अनुवर्ती प्रश्न है: पायथन की os.path.commonprefix की गिरावट को कैसे दरकिनार किया जाए?

PPPPS : पायथन 3.4 के साथ शुरू, हमारे पास पाथलिब है , एक मॉड्यूल है जो एक मार्ग मार्ग हेरफेर वातावरण प्रदान करता है। मेरा अनुमान है कि रास्तों के एक समूह का सामान्य उपसर्ग प्रत्येक पथ के सभी उपसर्गों को प्राप्त करने के साथ प्राप्त किया जा सकता है (साथ PurePath.parents()), इन सभी माता-पिता के सेट के प्रतिच्छेदन को लेते हुए, और सबसे लंबे आम उपसर्ग का चयन करें।

PPPPPS : पायथन 3.5 ने इस प्रश्न का उचित समाधान पेश किया: os.path.commonpath()जो एक वैध मार्ग देता है।


वास्तव में मुझे क्या चाहिए। आपके तत्काल उत्तर के लिये धन्यवाद। समय की पाबंदी हटने पर आपका उत्तर स्वीकार कर लिया जाएगा।
तमकिस्क्वार 18

10
ध्यान रखें commonprefix, जैसे कि सामान्य उपसर्ग के लिए /usr/var/logऔर /usr/var2/logजैसा कि लौटाया जाता है /usr/var- जो संभवतः वह नहीं है जिसकी आप अपेक्षा करेंगे। (इसके लिए यह संभव है कि वे ऐसे मार्ग लौटाएं जो वैध निर्देशिका नहीं हैं।)
पीटर ब्रिग्स

@PeterBriggs: धन्यवाद, यह चेतावनी महत्वपूर्ण है। मैंने एक पीपीएस जोड़ा।
एरिक ओ लेबिगोट

1
@ ईओएल: मैं वास्तव में स्लैश को जोड़कर समस्या को ठीक करने का तरीका नहीं देखता :( अगर हमारे पास है ['/usr/var1/log/', '/usr/var2/log/']?
bluenote10

1
@ ईओएल: चूंकि मैं इस समस्या के लिए एक आकर्षक समाधान खोजने में असफल रहा, हालांकि इस उप-मुद्दे पर एक अलग प्रश्न पर चर्चा करना ठीक हो सकता है ।
bluenote10

86

os.path.relpath:

एक रिश्तेदार फ़ाइलपथ को वर्तमान निर्देशिका से या वैकल्पिक आरंभ बिंदु से वापस लाने के लिए।

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

2
की उपस्थिति के os.pardirलिए जाँच से अधिक मजबूत है ..(के लिए जाँच , वहाँ कई अन्य सम्मेलनों, हालांकि नहीं हैं)।
एरिक ओ लेबिगॉट

8
क्या मैं गलत हूं या यह os.relpathअधिक शक्तिशाली है क्योंकि यह संभालता है ..और PurePath.relative_to()नहीं करता है? क्या मैं कुछ भूल रहा हूँ?
रे सलेमई

15

एक और विकल्प है

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

यह हमेशा एक सापेक्ष मार्ग लौटाता है; यह सीधे इंगित नहीं करता है कि क्या एक पथ दूसरे के ऊपर है (एक os.pardirदो संभावित परिणामी सापेक्ष रास्तों के सामने उपस्थिति की जांच कर सकता है , हालांकि)।
एरिक ओ लेबिगॉट

8

पायथॉन 3 में, पैथलिब का उपयोग करते हुए, जेम के सुझाव का लेखन।

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'

तो dir1.relative_to(dir2)अगर वे एक ही हैं तो PosixPath ('।') दे देंगे। जब आप उपयोग करते हैं if dir2 in dir1.parentsतो यह पहचान के मामले को बाहर कर देता है। यदि कोई पथों की तुलना कर रहा है और relative_to()यदि वे पथ-संगत हैं तो चलाना चाहते हैं, तो बेहतर समाधान हो सकता है if dir2 in (dir1 / 'x').parentsया हो सकता है if dir2 in dir1.parents or dir2 == dir1। फिर पथ संगतता के सभी मामलों को कवर किया जाता है।
ingyhere

3

शुद्ध पायथन 2 w / o 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 "."

यह एक अच्छा लग रहा है, लेकिन, जैसा कि मैं ठोकर खाता हूं, एक मुद्दा है जब cwdऔर pathसमान होते हैं। यह पहले जांच लेना चाहिए कि क्या वे दोनों एक ही हैं और ""या तो वापस लौट आए या"."
सीन पोपिक

1

संपादित करें: 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):])

यह एक स्ट्रिंग है, लेकिन आप निश्चित रूप से इसे अन्य पथ ऑब्जेक्ट के निर्माता के रूप में उपयोग कर सकते हैं।


4
यह उससे भी आसान है: बस parent in son.parents, और अगर यह है, शेष के साथ हो रही है son.relative_to(parent)
jme

@jme आप का जवाब बेहतर है, आप इसे पोस्ट क्यों नहीं करते?
जेरेमी कोचॉय
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.