한두번이야 diff 나 winmerge 같은 프로그램을 사용해서 어떤 파일이 다른지 알아내면 되지만,
비교 작업을 여러번해야한다면, 매번 비교하는 것도 꽤나 시간적인 부담이 됩니다.
이럴때 필요한 것이 똑똑한 DIFF 입니다.
원리는 간단한데...
파일 비교에 필요한 몇가지 정보를 미리 구축해놓고 구축된 파일만 비교하는 것입니다.
여기서 파일 비교에 필요한 정보는 CRC32, 수정시간, 크기 등이며,
이러한 정보들을 모아놓은 것을 CRC테이블이라고 부르도록 하겠습니다.
CRC테이블을 구축하기 위해서는 os.walk 로 돌면서 각 파일 정보를 뽑아내는 것입니다.
단, CRC 정보는 얻어내는데 부담이 있기 때문에 수정 시간이 다를 경우만 계산하도록 해야합니다.
주의해야할 문제가 있는데, 프로그램을 돌리다가 중단 할 경우에 대한 예외 처리입니다.
10G 나 되는 데이터에 대해 CRC 테이블을 구축하다가 9.9G에서 중단했는데, 처음부터 다시하려면
좀 벅차죠; 이를 대비해서 매 파일 처리시마다 작업중인 목록을 저장해둬야 합니다.
이전 작업 상황을 읽어드리는 경우에도 하나 더 주의해야할 점이 있습니다.
9.9G 까지 처리하다 중단하고, 다시 실행했는데 이번에는 1G 만 처리하다 중단 하는 경우입니다.
이런 문제를 막기 위해서는 이전 작업중인 목록을 읽어드린 직후, CRC테이블에 업데이트 시켜야 합니다.
import os
import binascii
import cPickle
import ctypes
import stat
def Main(ignorePaths, ignoreDirNames, ignoreFileNames, ignoreFileExts):
try:
crcDict = cPickle.load(open("_crctable", "r"))
except IOError:
crcDict = {}
try:
workingData = open("_crctable.working", "r").read()
workingList = workingData.splitlines()
for workingLine in workingList:
fileCRC, fileMTime, fileSize, filePath = workingLine.split(":")
crcDict[filePath] = (int(fileCRC, 16), float(fileMTime), int(fileSize))
cPickle.dump(crcDict, open("_crctable", "w"))
except IOError:
pass
outDict = {}
working = open("_crctable.working", "w")
for curDirName, childDirNames, fileNames in os.walk("."):
removeDirNames = []
for childDirName in childDirNames:
if childDirName.lower() in ignoreDirNames:
removeDirNames.append(childDirName)
for removeDirName in removeDirNames:
childDirNames.remove(removeDirName)
curDirName = curDirName.lower()
for fileName in fileNames:
fileName = fileName.lower()
if fileName in ignoreFileNames:
continue
if os.path.splitext(fileName)[1] in ignoreFileExts:
continue
filePath = os.sep.join((curDirName, fileName))
if filePath in ignorePaths:
continue
oldFileCRC, oldFileMTime, oldFileSize = crcDict.get(filePath, (0, 0, 0))
newFileMTime = os.stat(filePath).st_mtime
if newFileMTime == oldFileMTime:
outDict[filePath] = (oldFileCRC, oldFileMTime, oldFileSize)
working.write("0x%.8x:%f:%d:%s\n" % (oldFileCRC, oldFileMTime, oldFileSize, filePath))
working.flush()
else:
print("build_crc32: %s" % (filePath))
fileData = open(filePath, "rb").read()
newFileCRC = ctypes.c_ulong(binascii.crc32(fileData)).value
newFileSize = len(fileData)
outDict[filePath] = (newFileCRC, newFileMTime, newFileSize)
print("\tcrc : %.8x" % newFileCRC)
print("\tsize: %d" % newFileSize)
print("\ttime: %f" % newFileMTime)
working.write("0x%.8x:%f:%d:%s\n" % (newFileCRC, newFileMTime, newFileSize, filePath))
working.flush()
working.close()
os.remove("_crctable.working")
cPickle.dump(outDict, open("_crctable", "w"))
if __name__ == "__main__":
import sys
mainPath = sys.argv[0]
mainName = os.path.split(mainPath)[1]
mainNameBody = os.path.splitext(mainName)[0]
ignorePaths = set()
ignoreDirNames = set()
ignoreFileNames = set()
ignoreFileExts = set()
ignorePaths.add(".\\%s" % mainName.lower())
ignorePaths.add(".\\%s.ignores" % mainName.lower())
ignorePaths.add(".\\%s.ignores~" % mainName.lower())
ignoreFileNames.add("_crctable.working")
ignoreFileNames.add("_crctable")
ignoreFileExts.add(".swp")
ignoreFileExts.add(".py~")
try:
for line in open("%s.ignores" % (mainNameBody)).read().splitlines():
type, value = line.split(":")
value = value.lower()
if type == 'f':
print "ignore_file_name:", value
ignoreFileNames.add(value)
elif type == 'd':
print "ignore_dir_name:", value
ignoreDirNames.add(value)
elif type == 'e':
print "ignore_file_ext:", value
ignoreFileExts.add(value)
except IOError:
pass
Main(ignorePaths, ignoreDirNames, ignoreFileNames, ignoreFileExts)


python 을 좋아하는 게임 프로그래머