[샘플 추가]32자리 16진수를 비교하려면 어떻게 해야 빠를까요?

정의석   
   조회 3722   추천 0    

Hash_Test.zip (6.3M), Down : 5, 2021-07
Hash_Test.z01 (9.9M), Down : 5, 2021-07
Hash_Test.z02 (9.9M), Down : 5, 2021-07
Hash_Test.z03 (9.9M), Down : 6, 2021-07

32자리로 이루어진 16진수(MD5값 입니다.)가 약 100만건 정도 있습니다.

이 값을 다른 MD5 100만건 정도와 비교하는 일을 해야 합니다.

엑셀에서 VLookup으로 하니 속도가 너무 느려서요...(속도가 느리다기 보다는 뻗어 버리네요.. ㅠㅠ)

이걸 DB에 넣어서 쿼리로 비교 하거나, 아니면 DB없이 Application(VC, VB, 파이썬 등..)으로 변수(배열)에 넣어서 비교하거나... 어떻게 하는게 빠를까요?

----------------

염칠불구하고 샘플파일을 올렸습니다.

hash_test_1.csv와 hash_test_2.htm을 비교하려 합니다. (htm파일을 csv로 변환한 파일도 같이 첨부 합니다. htm에서 직접 작업이 불편하면 csv로 해도 상관 없습니다.)

이 두 파일의 row 개수가 다릅니다. 그래서 두번 비교를 해야할듯 합니다.

짧은글 일수록 신중하게.
the촌놈 2021-07
100만개라는 cell 갯수가 문제인 것 같습니다. 파이썬등으로 적당히 잘라서 쓰레드로 처리하면.... 되지 않을까요?
     
정의석 2021-07
작년에 파이썬 책 한권을 훑어 보기는 했는데,, 쓰레드.. 한번 공부 해 보겠습니다.
엑셀은 절대 비추입니다. 코드 짜실거면 정렬 후 이진탐색 하시면 될것 같네요.
     
송주환 2021-07
+1
이미 데이터가 스프레드시트 형태로 되어 있으면, CSV로 익스포트 하면 간단하게 불러올 수 있습니다.
          
정의석 2021-07
데이터 파일 하나는 html이고, 하나는 csv입니다.
그런데 이 html파일도 단순한 테이블 형태라서 엑셀에서 부르면 바로 스프레드쉬트 형태로 불러와지고, 이를 Powershell을 통해서 csv등으로 저장하는 것은 테스트 해 봤습니다.
     
정의석 2021-07
네.. 엑셀은 안되겠더라고요..
정렬 알고리즘을 배운지가 오래돼서 잘 기억이 안나는데,, 정렬을 하고나면 이진탐색이 빠르다는 말씀이신거죠?
chis 2021-07
db에 넣는게 제일 편할거같은 느낌적인 느낌입니다.
     
정의석 2021-07
DB가 빠를거라는 느낌적인 느낌은 있었는데, DB자체로는 안되거 SSMS같은걸로 그때그때 쿼리를 날려주거나,, 아니면 그 DB를 이용하는 프로그램을 하나 짜거나 해야할거 같아서요.. 사무실에서 사용할게 아니라 외부에서 간편하게 사용해야 하는지라 어떤 방법이 좋을지 고민중입니다.
트니아빠 2021-07
엑셀이 가로 1,048,576행까지만 지원이 된다고 하니, 이보다 크면 무조건 다른 방법을 이용하는게 맞을 것 같습니다.
동일한 값이 양측에 존재하느냐 존재하지 않느냐 등등의 조건에 맞추어 코드를 짜면 될 것 같습니다.
     
정의석 2021-07
네. 엑셀의 한계 때문에 100만 라인 이상은 일단 고려대상에서 제외 했습니다. (원본 데이터가 csv형태로 있습니다.)
대부분의 경우 두개의 데이터셋이 같을텐데, 가끔 잘못된 값이 들어가는 경우가 있어서 이걸 걸러내기 위한 작업입니다.
epowergate 2021-07
sort && diff
30초면 나올것 같은데요
새옹지마아 2021-07
저라면 DB씁니다.
그 정도 건수면 해시 값에 인덱스만 달아주면
0.1초 안에 답 나옵니다.
컴 성능 좋으면 0.01초.
     
정의석 2021-07
인덱스를 달아주면 빠르다...는건 알아봤는데,
처음에 100만건의 데이터를 인덱스 달아서 입력하는데도 그리 오래 걸리진 않겠죠?
일단 데이터를 DB에 넣는거 부터 해 보겠습니다.
viper9 2021-07
1. 두가지 입력데이터의 형태가 어떤지, (파일인지 어떤 식인지...)
2. 무엇을 비교해야하는지
3. 결과로 무엇을 어떤 형태로 출력해야하는지

이것이 명확해야할듯합니다.

DB에 넣어서 SQL로 돌려도 되고, 간단한 프로그램을 짜서 돌려도 되죠.
전 C++ 프로그래머인데... C++로 해도되긴하는데 이런건 파이썬으로 간단히 스크립트 만드는게 제일 좋지 않나 싶습니다.

찾아보니 파이썬에서 100만라인의 파일 정도는 열 수 있는듯합니다.
     
정의석 2021-07
1. 특정 문자열이 있고, 이 문자열이 가리키는 내용에 대한 MD5 해시값이 하나는 HTML, 하나는 CSV로 있습니다.
2. 이 두개의 파일 내용이 같아야 하는데, 가끔 내용이 다른경우가 있어서요..
3. 두개의 파일중 어느쪽이 맞는지 모르기 때문에 다른 부분의 해당 문자열과 그 해시값을 알려주면 됩니다.

DB를 사용하게 되면 DB엔진을 포함해야 해서 덩치가 커지는 문제때문에 일단 프로그램을 하나 짜는걸 우선으로 생각하고 있습니다.
100만개의 해시값이래봤자 용량으로는 얼마 안되니 파일을 변수로 읽어들여서 비교하면 되지 않을까... 정도 생각하고 있습니다.
이일저일 2021-07
제가 해야 사는 일이라면
한 두번 돌린다면 파이썬으로
계속 돌려야하면 C나 C++을
한쪽 비교대상이 많고 변동이 별로 없다면 db를 쓸 것 같네요.
일에 대해서 좀더 자세히 안다면 달라질 수 있겠지만 비교라는게 정확히 어떤건지 모호해서..
     
정의석 2021-07
1. 특정 문자열이 있고, 이 문자열이 가리키는 내용에 대한 MD5 해시값이 하나는 HTML, 하나는 CSV로 있습니다.
2. 이 두개의 파일 내용이 같아야 하는데, 가끔 내용이 다른경우가 있어서요..
3. 두개의 파일중 어느쪽이 맞는지 모르기 때문에 다른 부분의 해당 문자열과 그 해시값을 알려주면 됩니다.

DB를 사용하게 되면 DB엔진을 포함해야 해서 덩치가 커지는 문제때문에 일단 프로그램을 하나 짜는걸 우선으로 생각하고 있습니다.
이 두개의 파일의 내용은 거의(99.999%) 비슷하지만, 한두개 정도 달라지는 경우가 있어서요.
이 두개의 파일은 비슷한 내용을 담고 있지만, 어느 한쪽(html이나 csv)의 값이 고정되어 있지는 않습니다.
          
이일저일 2021-07
위의 내용을 조합해 보면..
DB는 제외.

POC로 python을 이용해서 HTML_loader(), CSV_loader()를 통해서 리스트에 MD5를 쭉 읽어들이고...
정렬된 두 list를 하나씩 옮기면서 비교하면 one-pass로 끝날 수 있을 것 같네요.

처음엔 dict 같은 걸 써야 하나 생각했는데... 그럴 필요는 없을 것 같고요. 오리려 느려질 수 있을 것 같네요.

그 잘 돌아가고 속도가 필요한 상황이 되면 C++로 vector를 쓰는 버젼으로 porting하면 큰 노력없이 포팅이 가능할 것 같네요.
          
이일저일 2021-07
오후 반차라서 병원 갔다가 집에서 쉬면서 간단하게 한 번 작성해 봤습니다.
파일 읽는 부분은 포맷 문제도 있고 해서 건너뛰고... 랜덤한 100만개 리스트를 만들고, 복사하고, 에러를 만들고, 비교하는 코드만 짜 봤습니다.

import random


def generate_random_list(size=1000000):
    a_list = ["{0:032x}".format(random.randint(0, 0x100000000000000000000000000000000)) for x in range(size)]
    return a_list


def compare_sorted_list(list1, list2):
    index1 = 0
    index2 = 0
    while index1 < len(list1) or index2 < len(list2):
        if list1[index1] == list2[index2]:
            index1 += 1
            index2 += 1
        elif list1[index1] < list2[index2]:
            print("found unmatched entry on ({0}, {1})".format(index1, index2))
            index1 += 1
        else:
            print("found unmatched entry on ({0}, {1})".format(index1, index2))
            index2 += 1


if __name__ == '__main__':
    # generate sample data set
    list1 = generate_random_list(1000000)
    list2 = list1.copy()
    #  make noise
    del list2[random.randint(0, len(list2))]
    del list1[random.randint(0, len(list1))]
    #  sort
    list1.sort()
    list2.sort()
    #  compare
    compare_sorted_list(list1, list2)
               
정의석 2021-07
바쁘신 와중에 코드를 직접 작성 해 주셔서 감사합니다.
list1.sort()
list2.sort()
이 부분에서 CSV의 첫 행이 같이 정렬이 되어버리네요... ㅠㅠ
작성해 주신 코드를 토대로 제가 좀 더 살펴보겠습니다.
          
이일저일 2021-07
오늘은 퇴근하고 나서 저녁 먹고 느긋하게 로드하는 함수도 넣어 봤습니다. 비교는 금방 되는 것 같은데... 파일 로딩이 시간이 많이 걸리네요.
그나마 csv는 좀 낫고, html은 훨씬 느리네요.

# !/usr/bin/python3

def load_from_csv(file_name):
    with open(file_name, 'r', encoding='latin-1') as csv_file:
        rows = list()
        for line in csv_file:
            columns = line.strip().split(',')
            rows.append(columns)
    del rows[0]
    return rows


def load_from_html(file_name):
    with open(file_name, 'r', encoding='latin-1') as html_file:
        rows = []
        for line in html_file:
            line = line.strip()
            if line.startswith('<tr'):
                columns = []
            elif line.startswith('<td'):
                close_chevron = line.find('>')
                open_chevron = line.rfind('<')
                columns.append(line[close_chevron + 1:open_chevron])
            elif line.startswith('</tr'):
                rows.append(columns)
    del rows[0]
    del rows[-1]
    return rows


def left_entry_key(left_entry):
    return (left_entry[0], left_entry[8])


def right_entry_key(right_entry):
    return (right_entry[1], right_entry[7])


def compare_sorted_list(list1, list2, key_for_list1, key_for_list2):
    index1 = 0
    index2 = 0
    while index1 < len(list1) or index2 < len(list2):
        entry1 = list1[index1]
        entry2 = list2[index2]
        key1 = key_for_list1(entry1)
        key2 = key_for_list2(entry2)
        if key1 == key2:
            index1 += 1
            index2 += 1
        elif key1 < key2:
            print("mismatch found from list1: {0}".format(entry1))
            index1 += 1
        else:
            print("mismatch found from list2: {0}".format(entry2))
            index2 += 1


if __name__ == '__main__':
    list1 = load_from_csv('hash_test_1.csv')
    list2 = load_from_csv('hash_test_2.csv')

    # sort
    list1.sort(key=left_entry_key)
    list2.sort(key=right_entry_key)

    #  compare
    compare_sorted_list(list1, list2, left_entry_key, right_entry_key)
               
정의석 2021-07
WoW~ 잘 동작 합니다.
html이 느리다고 하셨는데, 파워쉘로 html을 csv로 변환하는 시간보다는 빠른거 같습니다.
이걸로 출력 부분만 제 입맛에 맞게 수정하면 될거 같습니다.
감사합니다. ^^
                    
이일저일 2021-07
비교하는 쪽을 조금만 수정하면 좀더 깔끔하게 나올 것 같은데..
자꾸 속도만 신경 쓰이고 깔끔하게 만드는 건 신경이 안 쓰이네요.
제 일이 아니라서 그런가 봅니다. ㅎㅎ
정의석 2021-07
일단 간단하게라도 제가 먼저 해 보면 될것을 해보지도 않고 회원님들께 질문을 드린거 같아 송구스럽습니다.
답변 달아주신 내용을 보면 100만개의 MD5 해시값 정도의 비교는 그리 오래 걸리지 않는다고 하니 일단 무식하게라도 파이썬같은걸로 한번 해 보겠습니다.
혹시 파이썬에서 속도등의 이슈가 있으면 다시 질문드리겠습니다. (아마 정렬 알고리즘에 대한 추천등을 문의드리게 될거 같습니다.)
쓰레기단장 2021-07
File Comparision 이네요.

1. DB에 넣을라면 어차피 임포터를 만들어야 할겁니다. CSV정도라면 그냥 들어가겠지만 HTML은 포맷에 따라 파서가 있어야겠죠. 그런데 파서만들고 DB에 넣는 수고를 할 바에야 그냥 C++ 혹은 C#으로 짜는게 더 나을듯해요.
2.  파이썬도 나쁘지않겠지만 이 녀석은 반쯤 인터프리터라서 루프는 당연히 네이티브보다 느릴겁니다. 그래도 몇 초로 끝나지 싶습니다.
3. 비교... 라고 하셨는데, 만약 제가 이해한대로 MD5가 배열형태로 쭉 있고 두 배열의 인덱스별 값이 다 같은지 안같은지 비교하는 거라면 프로그램으로 간단하게 두 파일의 MD5값을 배열 2개에 집어넣고 for돌면서 같은지 검사하면 되지 싶습니다. 두 파일이 가지고 있는 MD5개수는 당연히 같겠죠?
4. 앞 계산 결과가 뒤의 계산결과에 영향을 주지 않으므로 스레드화 하면 더 빨라집니다만 백만개 정도면 싱글스레드로도 충분히 납득할 속도가 나올겁니다.
5. 혹시 파일 샘플을 공개 가능하시면 프로그램 정도는 짜 드릴 수 있어요.
     
정의석 2021-07
염치 불구하고 샘플파일 올렸습니다.
혹시 시간되시면 부탁 드립니다.
속도가 아주 느리지 않다면, 파이썬으로 하면 좋을거 같습니다.
트니아빠 2021-07
문자열을 기준으로 outer join으로 두 그룹을 묶어서 md5 hash 값이 동일하지 않은 것을 찾으면 될 것 같습니다.
R 이던 Python 이던 DB던.. HTML 포맷이 귀찮음을 차지하는 큰 요소일 것 같습니다.
MD5 hash는 해당이 없을 것이나 다른 부분에서 escape 문자가 있다면 그것도 귀찮은 요소가 될 것 같습니다.
     
트니아빠 2021-07
R입니다.
i7-2600@3.40GHz 에서 1초가 안걸립니다.
코드는 다음과 같습니다.
library("dplyr")
data1 = read.csv("hash_test_1.csv")
data2 = read.csv("hash_test_2.csv")
full_join(data1 %>% select(Name, Hash.Value),
          data2 %>% select(Name, Hash.Value),
          by = 'Name') %>%
  mutate(Hash.compare = ifelse(is.na(Hash.Value.x) , "different",
                                      ifelse(is.na(Hash.Value.y) , "different",
                                            ifelse(Hash.Value.x != Hash.Value.y , "different",
                                                    "same")))) %>%
  filter(Hash.compare == "different")

결과는 다음과 같습니다.
          Name                    Hash.Value.x                    Hash.Value.y Hash.compare
1  File-16390 d3bd118f274843c67c01e1bd19827591 d3bd118f274843c67c01e1bd19827592    different
2  File-44386 21eac3a6e908334aee22ca4fbb770779 21eac3a6e908334aee22ca4fbb770778    different
3  File-144073 666cd4d110a1f7cb0cfebad833551f13 666cd4d110a1f7cb0cfebad833551f14    different
4  File-151835 be3f23fcd9ecada56fba5831f77f920c                            <NA>    different
5  File-251270 48160880d1d68d889f15773f924b5a01 48160880d1d68d889f15773f924b5a0c    different
6  File-311714 a35daa7a22747a43a21159c6f9dce482                            <NA>    different
7  File-318794 440c4fda328c812b6c65e23ab75221ed                            <NA>    different
8  File-369237 d82a22968b23c58b55bedad76b5ee93b                            <NA>    different
9  File-472363 f7ed2cadb49a6222f1d228fe32283991 f7ed2cadb49a6222f1d228fe32283990    different
10 File-472480 d0ba73fed3b6adcaf940997e14c75cb3 d0ba73fed3b6adcaf940997e14c75cb2    different
11 File-472501                            <NA> 1a4c4847b8ec496cb7de207613aa6186    different
12 File-472502                            <NA> 1efbc3ae6d30dc0b90236135f02b5128    different
13 File-472503                            <NA> 33f30175ea9cdc22cbb8dc9907c1bf88    different
14 File-472500                            <NA> c0902962257017f87fdc473eefecae4a    different
15 File-472504                            <NA> c2afd22c1916c75863b8d4ed157e7e14    different
16 File-472499                            <NA> 11864ab6fe9b0411c13ed97f629319c5    different
17 File-472497                            <NA> 6eed60ef1cd5986ac0b28f15e16b42b2    different
18 File-472498                            <NA> 87185fa01decbbd1a668eb2be5c1aac8    different
          
정의석 2021-07
앗.. 제가 원하던 결과입니다. 시간도 1초라니.. <감격>
R은 몇년전에 한번 보고 안봤는데, 다시 한번 봐야겠습니다.
감사합니다. ^^
          
이일저일 2021-07
꽤 오래 지났지만 1초도 안 걸린다는 코멘트에 꽂혀서 C++로 여러가지 방식으로 해 봤는데 1.5초 이하로는 잘 안 되네요. ㅠㅠ
HW의 차이도 있을 수 있겠지만 R이 대단하네요.
chis 2021-07
일단 100만개의 md5를 만들어서 2개의 list안에 넣어놓고 단순 for문으로 (싱글스레드)
1번리스트에서 하나씩 빼서 2번리스트에서 매치되는거 있는지 (index뽑는방식으로)
파이썬으로 만들어서 돌려보니 e3 1220에서 1000개가 50초 걸리네요..
100만개는 시간이 꽤 걸릴듯 합니다..
     
chis 2021-07
import csv

file1=open("hash_test_1.csv")
file2=open("hash_test_2.csv")

data=csv.reader(file1)
data2=csv.reader(file2)

md5set1=list(x[8] for x in data)
md5set2=list(x[7] for x in data2)

file1.close()
file2.close()


for i in md5set1:
    try:
        md5set2.index(i)
    except:
        print(i+" 없음")


기능만 하면 될거같아서 쉬는 시간에 깨작거려봤습니다.
html은 파서 돌려야해서 그냥 csv로 작업해보니까 역시 시간은 좀 걸리네요..
          
정의석 2021-07
바쁘신 와중에 코드를 직접 작성 해 주셔서 감사합니다.
테스트 해 보니 정상적으로 동작은 하는데,,, 속도가 많이 느리네요..
데이터를 정렬 후 비교등의 방법을 좀 더 고민 해 보겠습니다.


QnA
제목Page 743/5730
2015-12   1791684   백메가
2014-05   5266768   정은준1
2016-10   5609   슬러그
2019-01   4910   행복하세
2014-06   27806   akfalles
2009-05   8945   김건우
2017-10   4039   페르세우스
2019-01   11602   Power멘솔
2016-10   4597   오로라
2012-08   7491   김종민
2014-06   5887   김건우
2017-11   3654   lovemiai
2015-10   4514   topschool
2009-06   13686   김주용s
2017-11   4065   컴박
2012-09   5360   김영기대전
2021-08   6714   김효수
01-12   947   불멸의샌디…
2012-09   5218   병맛폰
01-15   1033   김제연
2016-10   5388   이문영
2020-04   2607   규니