⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 difflib.py

📁 PHP写的图片查看器
💻 PY
📖 第 1 页 / 共 2 页
字号:
        >>> s.ratio()        1.0        >>>        SequenceMatcher computes and caches detailed information about the        second sequence, so if you want to compare one sequence S against        many sequences, use .set_seq2(S) once and call .set_seq1(x)        repeatedly for each of the other sequences.        See also set_seqs() and set_seq1().        """        if b is self.b:            return        self.b = b        self.matching_blocks = self.opcodes = None        self.fullbcount = None        self.__chain_b()    # For each element x in b, set b2j[x] to a list of the indices in    # b where x appears; the indices are in increasing order; note that    # the number of times x appears in b is len(b2j[x]) ...    # when self.isjunk is defined, junk elements don't show up in this    # map at all, which stops the central find_longest_match method    # from starting any matching block at a junk element ...    # also creates the fast isbjunk function ...    # note that this is only called when b changes; so for cross-product    # kinds of matches, it's best to call set_seq2 once, then set_seq1    # repeatedly    def __chain_b(self):        # Because isjunk is a user-defined (not C) function, and we test        # for junk a LOT, it's important to minimize the number of calls.        # Before the tricks described here, __chain_b was by far the most        # time-consuming routine in the whole module!  If anyone sees        # Jim Roskind, thank him again for profile.py -- I never would        # have guessed that.        # The first trick is to build b2j ignoring the possibility        # of junk.  I.e., we don't call isjunk at all yet.  Throwing        # out the junk later is much cheaper than building b2j "right"        # from the start.        b = self.b        self.b2j = b2j = {}        self.b2jhas = b2jhas = b2j.has_key        for i in xrange(len(b)):            elt = b[i]            if b2jhas(elt):                b2j[elt].append(i)            else:                b2j[elt] = [i]        # Now b2j.keys() contains elements uniquely, and especially when        # the sequence is a string, that's usually a good deal smaller        # than len(string).  The difference is the number of isjunk calls        # saved.        isjunk, junkdict = self.isjunk, {}        if isjunk:            for elt in b2j.keys():                if isjunk(elt):                    junkdict[elt] = 1   # value irrelevant; it's a set                    del b2j[elt]        # Now for x in b, isjunk(x) == junkdict.has_key(x), but the        # latter is much faster.  Note too that while there may be a        # lot of junk in the sequence, the number of *unique* junk        # elements is probably small.  So the memory burden of keeping        # this dict alive is likely trivial compared to the size of b2j.        self.isbjunk = junkdict.has_key    def find_longest_match(self, alo, ahi, blo, bhi):        """Find longest matching block in a[alo:ahi] and b[blo:bhi].        If isjunk is not defined:        Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where            alo <= i <= i+k <= ahi            blo <= j <= j+k <= bhi        and for all (i',j',k') meeting those conditions,            k >= k'            i <= i'            and if i == i', j <= j'        In other words, of all maximal matching blocks, return one that        starts earliest in a, and of all those maximal matching blocks that        start earliest in a, return the one that starts earliest in b.        >>> s = SequenceMatcher(None, " abcd", "abcd abcd")        >>> s.find_longest_match(0, 5, 0, 9)        (0, 4, 5)        If isjunk is defined, first the longest matching block is        determined as above, but with the additional restriction that no        junk element appears in the block.  Then that block is extended as        far as possible by matching (only) junk elements on both sides.  So        the resulting block never matches on junk except as identical junk        happens to be adjacent to an "interesting" match.        Here's the same example as before, but considering blanks to be        junk.  That prevents " abcd" from matching the " abcd" at the tail        end of the second sequence directly.  Instead only the "abcd" can        match, and matches the leftmost "abcd" in the second sequence:        >>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")        >>> s.find_longest_match(0, 5, 0, 9)        (1, 0, 4)        If no blocks match, return (alo, blo, 0).        >>> s = SequenceMatcher(None, "ab", "c")        >>> s.find_longest_match(0, 2, 0, 1)        (0, 0, 0)        """        # CAUTION:  stripping common prefix or suffix would be incorrect.        # E.g.,        #    ab        #    acab        # Longest matching block is "ab", but if common prefix is        # stripped, it's "a" (tied with "b").  UNIX(tm) diff does so        # strip, so ends up claiming that ab is changed to acab by        # inserting "ca" in the middle.  That's minimal but unintuitive:        # "it's obvious" that someone inserted "ac" at the front.        # Windiff ends up at the same place as diff, but by pairing up        # the unique 'b's and then matching the first two 'a's.        a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.isbjunk        besti, bestj, bestsize = alo, blo, 0        # find longest junk-free match        # during an iteration of the loop, j2len[j] = length of longest        # junk-free match ending with a[i-1] and b[j]        j2len = {}        nothing = []        for i in xrange(alo, ahi):            # look at all instances of a[i] in b; note that because            # b2j has no junk keys, the loop is skipped if a[i] is junk            j2lenget = j2len.get            newj2len = {}            for j in b2j.get(a[i], nothing):                # a[i] matches b[j]                if j < blo:                    continue                if j >= bhi:                    break                k = newj2len[j] = j2lenget(j-1, 0) + 1                if k > bestsize:                    besti, bestj, bestsize = i-k+1, j-k+1, k            j2len = newj2len        # Now that we have a wholly interesting match (albeit possibly        # empty!), we may as well suck up the matching junk on each        # side of it too.  Can't think of a good reason not to, and it        # saves post-processing the (possibly considerable) expense of        # figuring out what to do with it.  In the case of an empty        # interesting match, this is clearly the right thing to do,        # because no other kind of match is possible in the regions.        while besti > alo and bestj > blo and \              isbjunk(b[bestj-1]) and \              a[besti-1] == b[bestj-1]:            besti, bestj, bestsize = besti-1, bestj-1, bestsize+1        while besti+bestsize < ahi and bestj+bestsize < bhi and \              isbjunk(b[bestj+bestsize]) and \              a[besti+bestsize] == b[bestj+bestsize]:            bestsize = bestsize + 1        if TRACE:            print "get_matching_blocks", alo, ahi, blo, bhi            print "    returns", besti, bestj, bestsize        return besti, bestj, bestsize    def get_matching_blocks(self):        """Return list of triples describing matching subsequences.        Each triple is of the form (i, j, n), and means that        a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in        i and in j.        The last triple is a dummy, (len(a), len(b), 0), and is the only        triple with n==0.        >>> s = SequenceMatcher(None, "abxcd", "abcd")        >>> s.get_matching_blocks()        [(0, 0, 2), (3, 2, 2), (5, 4, 0)]        """        if self.matching_blocks is not None:            return self.matching_blocks        self.matching_blocks = []        la, lb = len(self.a), len(self.b)        self.__helper(0, la, 0, lb, self.matching_blocks)        self.matching_blocks.append( (la, lb, 0) )        if TRACE:            print '*** matching blocks', self.matching_blocks        return self.matching_blocks    # builds list of matching blocks covering a[alo:ahi] and    # b[blo:bhi], appending them in increasing order to answer    def __helper(self, alo, ahi, blo, bhi, answer):        i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)        # a[alo:i] vs b[blo:j] unknown        # a[i:i+k] same as b[j:j+k]        # a[i+k:ahi] vs b[j+k:bhi] unknown        if k:            if alo < i and blo < j:                self.__helper(alo, i, blo, j, answer)            answer.append(x)            if i+k < ahi and j+k < bhi:                self.__helper(i+k, ahi, j+k, bhi, answer)    def get_opcodes(self):        """Return list of 5-tuples describing how to turn a into b.        Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple        has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the        tuple preceding it, and likewise for j1 == the previous j2.        The tags are strings, with these meanings:        'replace':  a[i1:i2] should be replaced by b[j1:j2]        'delete':   a[i1:i2] should be deleted.                    Note that j1==j2 in this case.        'insert':   b[j1:j2] should be inserted at a[i1:i1].                    Note that i1==i2 in this case.        'equal':    a[i1:i2] == b[j1:j2]        >>> a = "qabxcd"        >>> b = "abycdf"        >>> s = SequenceMatcher(None, a, b)        >>> for tag, i1, i2, j1, j2 in s.get_opcodes():        ...    print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %        ...           (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))         delete a[0:1] (q) b[0:0] ()          equal a[1:3] (ab) b[0:2] (ab)        replace a[3:4] (x) b[2:3] (y)          equal a[4:6] (cd) b[3:5] (cd)         insert a[6:6] () b[5:6] (f)        """        if self.opcodes is not None:            return self.opcodes        i = j = 0        self.opcodes = answer = []        for ai, bj, size in self.get_matching_blocks():            # invariant:  we've pumped out correct diffs to change            # a[:i] into b[:j], and the next matching block is            # a[ai:ai+size] == b[bj:bj+size].  So we need to pump            # out a diff to change a[i:ai] into b[j:bj], pump out            # the matching block, and move (i,j) beyond the match            tag = ''            if i < ai and j < bj:                tag = 'replace'            elif i < ai:                tag = 'delete'            elif j < bj:                tag = 'insert'            if tag:                answer.append( (tag, i, ai, j, bj) )            i, j = ai+size, bj+size            # the list of matching blocks is terminated by a            # sentinel with size 0            if size:                answer.append( ('equal', ai, i, bj, j) )        return answer    def ratio(self):        """Return a measure of the sequences' similarity (float in [0,1]).        Where T is the total number of elements in both sequences, and        M is the number of matches, this is 2,0*M / T.        Note that this is 1 if the sequences are identical, and 0 if        they have nothing in common.        .ratio() is expensive to compute if you haven't already computed        .get_matching_blocks() or .get_opcodes(), in which case you may        want to try .quick_ratio() or .real_quick_ratio() first to get an        upper bound.        >>> s = SequenceMatcher(None, "abcd", "bcde")        >>> s.ratio()        0.75        >>> s.quick_ratio()        0.75        >>> s.real_quick_ratio()        1.0        """        matches = reduce(lambda sum, triple: sum + triple[-1],                         self.get_matching_blocks(), 0)        return 2.0 * matches / (len(self.a) + len(self.b))    def quick_ratio(self):        """Return an upper bound on ratio() relatively quickly.        This isn't defined beyond that it is an upper bound on .ratio(), and        is faster to compute.        """        # viewing a and b as multisets, set matches to the cardinality        # of their intersection; this counts the number of matches        # without regard to order, so is clearly an upper bound        if self.fullbcount is None:            self.fullbcount = fullbcount = {}            for elt in self.b:                fullbcount[elt] = fullbcount.get(elt, 0) + 1        fullbcount = self.fullbcount        # avail[x] is the number of times x appears in 'b' less the        # number of times we've seen it in 'a' so far ... kinda        avail = {}        availhas, matches = avail.has_key, 0        for elt in self.a:            if availhas(elt):                numb = avail[elt]            else:                numb = fullbcount.get(elt, 0)            avail[elt] = numb - 1            if numb > 0:                matches = matches + 1        return 2.0 * matches / (len(self.a) + len(self.b))    def real_quick_ratio(self):        """Return an upper bound on ratio() very quickly.        This isn't defined beyond that it is an upper bound on .ratio(), and        is faster to compute than either .ratio() or .quick_ratio().        """        la, lb = len(self.a), len(self.b)        # can't have more matches than the number of elements in the        # shorter sequence        return 2.0 * min(la, lb) / (la + lb)def get_close_matches(word, possibilities, n=3, cutoff=0.6):    """Use SequenceMatcher to return list of the best "good enough" matches.    word is a sequence for which close matches are desired (typically a    string).    possibilities is a list of sequences against which to match word    (typically a list of strings).    Optional arg n (default 3) is the maximum number of close matches to    return.  n must be > 0.    Optional arg cutoff (default 0.6) is a float in [0, 1].  Possibilities    that don't score at least that similar to word are ignored.    The best (no more than n) matches among the possibilities are returned    in a list, sorted by similarity score, most similar first.    >>> get_close_matches("appel", ["ape", "apple", "peach", "puppy"])    ['apple', 'ape']    >>> import keyword    >>> get_close_matches("wheel", keyword.kwlist)    ['while']    >>> get_close_matches("apple", keyword.kwlist)    []    >>> get_close_matches("accept", keyword.kwlist)    ['except']    """    if not n >  0:        raise ValueError("n must be > 0: " + `n`)    if not 0.0 <= cutoff <= 1.0:        raise ValueError("cutoff must be in [0.0, 1.0]: " + `cutoff`)    result = []    s = SequenceMatcher()    s.set_seq2(word)    for x in possibilities:        s.set_seq1(x)        if s.real_quick_ratio() >= cutoff and \           s.quick_ratio() >= cutoff and \           s.ratio() >= cutoff:            result.append((s.ratio(), x))    # Sort by score.    result.sort()    # Retain only the best n.    result = result[-n:]    # Move best-scorer to head of list.    result.reverse()    # Strip scores.    # Python 2.x list comprehensions: return [x for score, x in result]    return_result = []    for score, x in result:        return_result.append(x)    return return_resultdef _test():    import doctest, difflib    return doctest.testmod(difflib)if __name__ == "__main__":    _test()

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -