두 개의 바이너리 파일이 있습니다.
하나는 수백 킬로그램이고 다른 하나는 몇 기가바이트입니다.
작은 파일 전체가 큰 파일 내에 포함되어 있는지 알고 싶습니다. 그렇다면 큰 파일의 시작 부분에서 오프셋은 얼마입니까?
나는 정확한 일치에만 관심이 있습니다. 즉, 전체 파일이 다른 파일에 포함되어 있는지 여부입니다.
두 파일 모두 바이너리입니다.
이 작업을 수행할 수 있는 기존 도구/단일 라이너가 있습니까?
답변1
기존 도구가 생각나지 않습니다.
grep -F --binary --byte-offset --only-matching
충분히 가까워 보이지만 -F
. 그리고 cmp
문자 건너뛰기만 허용됩니다. diff
역시 별로 도움이 되지 않는 것 같습니다.
그러나 괜찮은 라이브러리를 갖춘 프로그래밍 언어의 일부 라인입니다. 예를 들어 Boost를 사용하는 C++ 프로그램은 다음과 같습니다.
#include <boost/algorithm/string/find.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <cassert>
#include <iostream>
using namespace boost;
using namespace boost::algorithm;
using namespace boost::iostreams;
using namespace std;
int main(int argc, char **argv)
{
if (argc != 3) {
cerr << "Call: " << argv[0] << " PATTERN_FILE SRC_FILE\n";
return 3;
}
mapped_file_source pattern(argv[1]);
mapped_file_source src(argv[2]);
iterator_range<const char*> p_range(pattern.data(),
pattern.data() + pattern.size());
iterator_range<const char*> s_range(src.data(), src.data() + src.size());
iterator_range<const char*> result = find_first(s_range, p_range);
if (result) {
size_t pos = result.begin()-s_range.begin();
cout << pos << '\n';
return 0;
}
return 1;
}
다음과 같이 컴파일할 수 있습니다(프로그램 소스가 로 저장되는 경우 find.cc
).
$ make CXXFLAGS="-Wall -g" LDLIBS="-lboost_iostreams" searchb
테스트하려면:
$ dd if=WTF_-_EPISODE_277_RACHAEL_HARRIS.mp3 of=t skip=232323 bs=1 count=4K
$ ls -l t
-rw-r--r-- 1 juser users 4096 2012-05-31 15:24 t
$ ./searchb t WTF_-_EPISODE_277_RACHAEL_HARRIS.mp3
232323
출력은 소스 파일의 일치하는 위치입니다.
파일이 포함되지 않은 경우 종료 상태는 입니다 1
.
고쳐 쓰다:그동안 저는 이 간단한 도구를 여러 언어(C/C++/Python/Rust/Go)로 구현했으며 이러한 구현을 내유틸리티 저장소. 찾다 searchb*
. Python 구현은 가장 짧은 구현이며 외부 종속성이 필요하지 않습니다.
답변2
우리는 생물정보학에서 항상 이 작업을 수행합니다. 단, 부분 일치도 원하고 두 부분이 얼마나 잘 일치하는지 알고 싶습니다.
BLAT는 내가 아는 가장 빠른 솔루션입니다.https://en.wikipedia.org/wiki/BLAT_(생물정보학)
인덱스를 구축하고 일단 인덱스를 생성하면 엄청나게 빠릅니다.
답변3
다음은 외부 파일에서 하위 문자열 검색을 수행하는 Python 스크립트입니다. 대본은 원래 작성자가 작성했습니다.캄란 칸그리고자신의 블로그에 게시. 파일에서 검색 문자열을 가져오고 표준 입력에서 검색하도록 약간 수정했습니다.
#!/usr/bin/env python
import locale
import os
import sys
import urllib2
def boyermoore_horspool(fd, needle):
nlen = len(needle)
nlast = nlen - 1
skip = []
for k in range(256):
skip.append(nlen)
for k in range(nlast):
skip[ord(needle[k])] = nlast - k
skip = tuple(skip)
pos = 0
consumed = 0
haystack = bytes()
while True:
more = nlen - (consumed - pos)
morebytes = fd.read(more)
haystack = haystack[more:] + morebytes
if len(morebytes) < more:
return -1
consumed = consumed + more
i = nlast
while i >= 0 and haystack[i] == needle[i]:
i = i - 1
if i == -1:
return pos
pos = pos + skip[ord(haystack[nlast])]
return -1
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.stderr.write("""Usage: horspool.py NEEDLE_FILE [URL]
Search for the contents of NEEDLE_FILE inside the content at URL.
If URL is omitted, search standard input.
If the content is found, print the offset of the first occurrence and return 0.
Otherwise, return 1.""")
sys.exit(2)
needle_file = open(sys.argv[1])
needle = needle_file.read()
needle_file.close
fd = urllib2.urlopen(sys.argv[2]) if len(sys.argv) > 2 else sys.stdin
offset = boyermoore_horspool(fd, needle)
if offset >= 0: print offset
else: sys.exit(1)
fd.close()
답변4
메모리가 충분하다면 큰 파일을 작은 파일 크기의 청크로 나누어 읽으세요. 각 청크를 읽은 후 마지막으로 읽은 두 청크를 연결하고 결과를 문자열로 검색합니다. Python 3.8+에서 코드는 다음과 같습니다.
def find_at_offset(large_fp, small_fp):
small = small_fp.read()
blocks = [b"", b""]
base = 0
while blk := large_fp.read(len(small)):
base += len(blocks[0])
del blocks[0]
blocks.append(blk)
offset = b"".join(blocks).find(small)
if offset != -1:
return base + offset
return -1
개념은 매우 간단하고 일반 C로 잘 변환되며 메모리 매핑과 같은 특별한 기능이 필요하지 않습니다. 구현에 따라 필요한 최소 사용 가능한 메모리는 작은 파일 크기의 3-5배입니다. 장점은 고도로 최적화된 단순 문자열 검색을 사용하기 때문에 매우 빠르다는 것입니다.