파이썬에서는 파일의 내용을 "고유하게"식별하는 방법으로 파일 행의 순서 불변 해시를 빠르게 계산하고 싶습니다. 이러한 파일은 예를 들어 select ... from table
의 출력이며 줄의 순서는 임의적입니다.Python의 순서 불변 해시
다음은 내가 원하는 (hashlib의 해셔 중 하나를 사용하여) 원하는 것을 달성하는 예제이지만, 줄을 정렬해야하는 번거 로움을 감수하고 있습니다. 라인을 정렬하는 것은 목표를 달성하는 것, 즉 파일의 라인 순서에 의존하지 않는 해시를 얻는 것입니다. 하지만 분명히 O (n * log (n)) 비용은 피하고 싶습니다. 파일이 훨씬 길 때.
def get_hexdigest(filename, hasher, blocksize=65536, order_invariant=False):
if not os.path.isfile(filename):
return None
if order_invariant:
with open(filename, 'r') as f:
for line in sorted(f):
hasher.update(line.encode())
else:
with open(filename, 'rb') as f:
while True:
buf = f.read(blocksize)
hasher.update(buf)
if len(buf) < blocksize:
break
return hasher.hexdigest()
1메가바이트, 50K 행 파일 :
%%time
get_hexdigest('some_file', hashlib.sha1())
# Wall time: 1.71 ms
그러나 :
그것을 할 수있는 더 좋은/빠른 방법은 무엇%%time
get_hexdigest('some_file', hashlib.sha1(), order_invariant=True)
# Wall time: 77.4 ms
?
in this answer에서 언급했듯이 Scala에는 Murmurhash를 기반으로하는 순서 불변 해시가 있지만 32 비트 버전의 mmh3 (내 용도로는 충돌 가능성이 높음)이라고 가정하고 일부 표준 라이브러리 C 또는 Cython에서 무언가를 구현하는 것이 아니라 Python으로 사용할 수 있습니다. Murmurhash3은 128 비트 버전이지만 x64와 x86의 출력은 다릅니다. 나는 기계 독립적 결과를 원합니다.
그래서, 요약, 내가 좋아하는 것 : 좋은 분산과
- 기계 구조
- 낮은 충돌 속도에 걸쳐 일관성있는 결과, 즉 적어도 128 비트 (하지만 난 할 해시가 필요하지 않습니다 암호화)
- 합리적으로 빠르며, 1MB, 50K 라인 파일의 경우 최소 5ms 미만입니다.
- 가능하면 PyPi 또는 Conda의 라이브러리로 쉽게 사용할 수 있습니다.
- 반복되는 파일을 사용하는 파일에 적합합니다. 따라서 모든 행마다 동일한 XOR을 수행하면 비동기입니다.
편집 및 메모 : 여러 의견에 덕분에, 위의 코드는 메모리에 라인을 정렬 업데이트됩니다. order_invariant is True
의 원래 버전이었다
with os.popen('sort {}'.format(filename)) as f:
for line in f:
hasher.update(line.encode(encoding='utf-8'))
return hasher.hexdigest()
(위에서 사용 된 파일) 연결된 벽 시간 후 238 밀리이었다. 이것은 이제 77 밀리 초로 감소되었지만 선을 정렬하지 않는 것보다 여전히 느립니다. 정렬하면 n 줄에 n * log (n) 비용이 추가됩니다.
줄을 읽을 때 인코딩이 (UTF-8로) 변환되고 'r'
또는 'rb'
인 경우에는 바이트가 아닌 문자열을 가져와야합니다. 필자는 파일에 ASCII 데이터 만 포함한다고 가정하고 싶지 않습니다. 'rb'
에서 읽으면 제대로 분할되지 않은 줄이 생길 수 있습니다. order_invariant
이 False 일 때 동일한 문제가 발생하지 않습니다. 그 이유는 파일을 분할 할 필요가 없기 때문이며, 따라서 가장 빠른 방법은 해시를 업데이트하기 위해 이진 데이터 청크를 버리는 것입니다.
첫째 개선 : 당신은 아마'의 행에 대한'사용하여 파이썬 라인을 sort' 수 정렬 (F)'대신 선 작업하는 경우, 측면 참고로 외부 프로세스 ... –
를 호출 (파일의 라인에 대한 _'order-invariant 해쉬에 기반) _ 왜 '주문에 민감한'경우에 버퍼를 사용하고 있습니까? '.herline()'출력으로'hasher '를 펌핑하면됩니다. – zwer
또한 전체 파일을 인코딩하고 줄을 분할 한 다음 모든 줄에'.encode'를 호출하는 것이 훨씬 빠릅니다 :'with open (...) as f : 줄의 정렬을 위해 (f.read () (. utf-8 '). split ('\ n ')) : hasher.update (line)'(예, 전체 파일을 메모리에로드하지만 *를 * 정렬해야 함). – Bakuriu