python转载[Rsync Algorithm]

本文介绍了一种使用纯Python实现的rsync算法,该算法能够高效地对比并同步两个文件之间的差异。通过示例展示了如何获取文件的弱校验和与强校验和,并利用这些信息生成补丁文件来更新旧版本文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

转自:http://code.activestate.com/recipes/577518-rsync-algorithm/?in=lang-python

 

代码:Tested in Python 2.5, 2.6, and 3.1. In 2.7, io.BufferedReader should yield the best throughput. On all other versions use __builtin__.open.

# !/usr/bin/env python
#
 -*- coding: utf-8 -*-
"""
This is a pure Python implementation of the [rsync algorithm](TM96).

[TM96] Andrew Tridgell and Paul Mackerras. The rsync algorithm.
Technical Report TR-CS-96-05, Canberra 0200 ACT, Australia, 1996.
http://samba.anu.edu.au/rsync/.

### Example Use Case: ###

    # On the system containing the file that needs to be patched
    >>> unpatched = open("unpatched.file", "rb")
    >>> hashes = blockchecksums(unpatched)

    # On the remote system after having received `hashes`
    >>> patchedfile = open("patched.file", "rb")
    >>> delta = rsyncdelta(patchedfile, hashes)

    # System with the unpatched file after receiving `delta`
    >>> unpatched.seek(0)
    >>> save_to = open("locally-patched.file", "wb")
    >>> patchstream(unpatched, save_to, delta)
"""

import  collections
import  hashlib

if   not (hasattr( __builtins__ " bytes " ))  or  str  is  bytes:
    
#  Python 2.x compatibility
     def  bytes(var,  * args):
        
try :
            
return   '' .join(map(chr, var))
        
except  TypeError:
            
return  map(ord, var)

__all__   =  [ " rollingchecksum " " weakchecksum " " patchstream " " rsyncdelta " ,
    
" blockchecksums " ]


def  rsyncdelta(datastream, remotesignatures, blocksize = 4096 ):
    
"""
    Generates a binary patch when supplied with the weak and strong
    hashes from an unpatched target and a readable stream for the
    up-to-date data. The blocksize must be the same as the value
    used to generate remotesignatures.
    
"""
    remote_weak, remote_strong 
=  remotesignatures

    match 
=  True
    matchblock 
=   - 1
    deltaqueue 
=  collections.deque()

    
while  True:
        
if  match  and  datastream  is   not  None:
            
#  Whenever there is a match or the loop is running for the first
             #  time, populate the window using weakchecksum instead of rolling
             #  through every single byte which takes at least twice as long.
            window  =  collections.deque(bytes(datastream.read(blocksize)))
            checksum, a, b 
=  weakchecksum(window)

        
try :
            
#  If there are two identical weak checksums in a file, and the
             #  matching strong hash does not occur at the first match, it will
             #  be missed and the data sent over. May fix eventually, but this
             #  problem arises very rarely.
            matchblock  =  remote_weak.index(checksum, matchblock  +   1 )
            stronghash 
=  hashlib.md5(bytes(window)).hexdigest()
            matchblock 
=  remote_strong.index(stronghash, matchblock)

            match 
=  True
            deltaqueue.append(matchblock)

            
if  datastream.closed:
                
break
            
continue

        
except  ValueError:
            
#  The weakchecksum did not match
            match  =  False
            
try :
                
if  datastream:
                    
#  Get the next byte and affix to the window
                    newbyte  =  ord(datastream.read( 1 ))
                    window.append(newbyte)
            
except  TypeError:
                
#  No more data from the file; the window will slowly shrink.
                 #  newbyte needs to be zero from here on to keep the checksum
                 #  correct.
                newbyte  =  0
                tailsize 
=  datastream.tell()  %  blocksize
                datastream 
=  None

            
if  datastream  is  None  and  len(window)  <=  tailsize:
                
#  The likelihood that any blocks will match after this is
                 #  nearly nil so call it quits.
                deltaqueue.append(window)
                
break

            
#  Yank off the extra byte and calculate the new window checksum
            oldbyte  =  window.popleft()
            checksum, a, b 
=  rollingchecksum(oldbyte, newbyte, a, b, blocksize)

            
#  Add the old byte the file delta. This is data that was not found
             #  inside of a matching block so it needs to be sent to the target.
             try :
                deltaqueue[
- 1 ].append(oldbyte)
            
except  (AttributeError, IndexError):
                deltaqueue.append([oldbyte])

    
#  Return a delta that starts with the blocksize and converts all iterables
     #  to bytes.
    deltastructure  =  [blocksize]
    
for  element  in  deltaqueue:
        
if  isinstance(element, int):
            deltastructure.append(element)
        
elif  element:
            deltastructure.append(bytes(element))

    
return  deltastructure


def  blockchecksums(instream, blocksize = 4096 ):
    
"""
    Returns a list of weak and strong hashes for each block of the
    defined size for the given data stream.
    
"""
    weakhashes 
=  list()
    stronghashes 
=  list()
    read 
=  instream.read(blocksize)

    
while  read:
        weakhashes.append(weakchecksum(bytes(read))[0])
        stronghashes.append(hashlib.md5(read).hexdigest())
        read 
=  instream.read(blocksize)

    
return  weakhashes, stronghashes


def  patchstream(instream, outstream, delta):
    
"""
    Patches instream using the supplied delta and write the resultantant
    data to outstream.
    
"""
    blocksize 
=  delta[0]

    
for  element  in  delta[ 1 :]:
        
if  isinstance(element, int)  and  blocksize:
            instream.seek(element 
*  blocksize)
            element 
=  instream.read(blocksize)
        outstream.write(element)


def  rollingchecksum(removed, new, a, b, blocksize = 4096 ):
    
"""
    Generates a new weak checksum when supplied with the internal state
    of the checksum calculation for the previous window, the removed
    byte, and the added byte.
    
"""
    a 
-=  removed  -  new
    b 
-=  removed  *  blocksize  -  a
    
return  (b  <<   16 |  a, a, b


def  weakchecksum(data):
    
"""
    Generates a weak checksum from an iterable set of bytes.
    
"""
    a 
=  b  =  0
    l 
=  len(data)
    
for  i  in  range(l):
        a 
+=  data[i]
        b 
+=  (l  -  i) * data[i]

    
return  (b  <<   16 |  a, a, b

 

 

测试:

# On the system containing the file that needs to be patched 
>>> unpatched = open("unpatched.file", "rb") 
>>> hashes = blockchecksums(unpatched) 
 
# On the remote system after having received `hashes` 
>>> patchedfile = open("patched.file", "rb") 
>>> delta = rsyncdelta(patchedfile, hashes) 
 
# System with the unpatched file after receiving `delta` 
>>> unpatched.seek(0) 
>>> save_to = open("locally-patched.file", "wb") 
>>> patchstream(unpatched, save_to, delta) 

 

rsync算法:http://www.cnblogs.com/itech/archive/2010/06/13/1757952.html

 

完!


 

Python可以使用pyrsync模块来实现rsync功能。pyrsync是一个纯Python编写的模块,它实现了rsync算法,并提供了一组函数来完整地应用rsync功能,而不是作为rsync的包装器。该模块的开发人员认为rsync的初始规范要求使用MD5哈希,因此他们使用Python的hashlib模块来计算文件的哈希值。通过pyrsync,你可以像使用rsync命令一样在Python中完成文件或目录的同步操作。 使用Python实现rsync的主要目的是为了方便多个项目之间文件/目录的同步。由于有多个项目和主机,直接使用rsync命令拷贝不太方便且效率较低,而使用Python来封装rsync可以更灵活地处理多个项目的同步需求。通过在Python代码中调用pyrsync模块提供的函数,你可以方便地遍历多个项目并进行文件或目录的同步操作。 要使用pyrsync实现rsync功能,可以按照以下思路进行操作: 1. 理解rsync算法。请自行了解rsync算法的原理和机制。 2. 对于本地拷贝,可以使用pyrsync提供的函数,例如使用rsync_delta函数来计算源目录/文件与目标目录/文件之间的差异,并使用rsync_patch函数将差异应用到目标目录/文件上。 3. 对于远程拷贝,可以使用rsync命令的远程拷贝功能,通过调用subprocess模块来在Python中执行rsync命令。例如,可以使用subprocess模块的run函数来执行类似于"rsync -av 源目录/文件 用户@主机:/目录"的命令。 通过以上思路和pyrsync模块的函数,你可以使用Python实现rsync功能,以满足多个项目之间文件/目录的同步需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [实现 rsync算法 的纯Python模块_python_代码_下载](https://download.youkuaiyun.com/download/qq_38334677/85581462)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Python封装rsync命令。实现批量(多进程)同步目录/文件](https://blog.youkuaiyun.com/weixin_40618452/article/details/107901525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值