• Home
  • About
    • lahuman photo

      lahuman

      열심히 사는 아저씨

    • Learn More
    • Facebook
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Python] File Random access

06 Feb 2018

Reading time ~3 minutes

Python] 대용량 파일 처리

파이썬에서 큰 파일(200MB)에 대하여 3가지 정규식을 기준으로 데이터를 추출 할 경우 다음과 같은 코드를 작성 할 수 있다.

코드 1

# -*-coding:utf-8-*-
import re
import time


def get_result(pattern, content):
    return pattern.findall(content)


def append_data(name, pattern, content):
    result = get_result(pattern, content)
    if result:
        dic[name] = result


if name == 'main':
    start = time.time()
    patterns = dict() 
    patterns['patternA'] = re.compile(
        r'SESSION: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)-:angry:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)\((.+?)\),\s',
        re.I)
    patterns['patternB'] = re.compile(r"(?:\d\`){4}\w{6}\`\d{4}\`\d{8}\`(?:\d{2}\:){2}\d{2}\`", re.I)
    patterns['patternC'] = re.compile(r"devname\=\S+\s(?:device_id|devid)\=\S+\s(?:log_id|logid)\=\S+\stype\=\S+\s",
                                       re.I)

    dic = dict() # RESULT DATA

    with open('log-sample/500000.log', 'r') as log:
        for content in log:
            for name, pattern in patterns.iteritems():
                append_data(name, pattern, content)

    end = time.time() - start  # end에 코드가 구동된 시간 저장

    print (dic["patternA"][0][0])
    print (" END TIME : [" + str(end) + "]")

결과는 각 정규식 별로 다르게 나온다. 1번 정규식은 4초, 2번 정규식은 6초, 3번 정규식은 8초 정도로 3개를 한번에 위의 코드같이 처리 할 경우 평균 16초의 시간이 걸린다.

코드 2

# -*-coding:utf-8-*-
import re
import time
from multiprocessing import Pool


def get_result(pattern, content):
    return pattern.findall(content)


def append_data(dic, pattern, content):
    result = get_result(pattern, content)
    if result:
        dic.append(result)


def processing(pattern):
    dic = []
    with open('log-sample/500000.log', 'r') as log:
        for content in log:
            append_data(dic, pattern, content)
    print (len(dic))


if name == '__main__':
    start = time.time()
    patterns = [] 
    patterns.append(re.compile(
        r'SESSION: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)->(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)\((.+?)\),\s',
        re.I))
    patterns.append(re.compile(r"(?:\d\`){4}\w{6}\`\d{4}\`\d{8}\`(?:\d{2}\:){2}\d{2}\`", re.I))
    patterns.append(re.compile(r"devname\=\S+\s(?:device_id|devid)\=\S+\s(?:log_id|logid)\=\S+\stype\=\S+\s",
                                       re.I))

    pool = Pool(processes=4)
    pool.map(processing, patterns)

    end = time.time() - start  # end에 코드가 구동된 시간 저장
    print ("END TIME : [" + str(end) + "]")

두번째 코드는 각 패턴별로 multiprocessing을 이용하여 실행하는 코드 이다.

이 경우 결과시간은 평균 10초의 시간이 걸린다.

코드 3

# -*-coding:utf-8-*-
import re
import time
import os
from multiprocessing import Pool


def get_result(pattern, content):
    return pattern.findall(content)


def append_data(dic, pattern, content):
    result = get_result(pattern, content)
    if result:
        dic.append(result)


filename = 'log-sample/500000.log'


def processing(args):

    pattern = args[0]
    position = args[1]
    reple_dic = args[2]
    start_offset = args[3]
    end_line = args[4]

    dic = []
    line_cnt = 0
    with open(filename, 'r') as log:
        log.seek(start_offset)
        for content in log:
            append_data(dic, pattern, content)
            line_cnt += 1
            if line_cnt == end_line:
                break;

    # TODO use Dic

    print (len(dic))

def to_list(*args):
    return list(args)

if name == '__main__':
    start = time.time()

    # Read in the file once and build a list of line offsets
    line_offset = []
    offset = 0
    with open(filename, 'r') as file:
        for line in file:
            line_offset.append(offset)
            offset += len(line)

    print "file Line Count : "+ str(len(line_offset))

    patterns = [] 
 
    patterns.append(to_list(re.compile(
        r'SESSION: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)->(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)\((.+?)\),\s',
        re.I), "0,1,2,3,4", dict(), 0, 250000))
    patterns.append(to_list(re.compile(
        r'SESSION: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)->(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)\((.+?)\),\s',
        re.I), "0,1,2,3,4", dict(), line_offset[250000], 250000))

    patterns.append(to_list(re.compile(r"(?:\d\`){4}\w{6}\`\d{4}\`\d{8}\`(?:\d{2}\:){2}\d{2}\`", re.I), "0,1,2,3,4", dict(), 0, 250000))
    patterns.append(to_list(re.compile(r"(?:\d\`){4}\w{6}\`\d{4}\`\d{8}\`(?:\d{2}\:){2}\d{2}\`", re.I), "0,1,2,3,4", dict(), line_offset[250000], 250000))

    patterns.append(to_list(re.compile(r"devname\=\S+\s(?:device_id|devid)\=\S+\s(?:log_id|logid)\=\S+\stype\=\S+\s", re.I), "0,1,2,3,4", dict(), 0, 250000))
    patterns.append(to_list(re.compile(r"devname\=\S+\s(?:device_id|devid)\=\S+\s(?:log_id|logid)\=\S+\stype\=\S+\s", re.I), "0,1,2,3,4", dict(), line_offset[250000], 250000))

    pool = Pool(processes=8)
    pool.map(processing, patterns)

    end = time.time() - start  # end에 코드가 구동된 시간 저장
    print ("END TIME : [" + str(end) + "]")

파일에 대하여 특정 ROW 부터 처리 하도록 병령 처리를 하면 3번 코드와 같은 형식으로 나온다.

이 경우 실행시간은 평균 5초 가 된다.

큰 파일에 대한 처리는 결국 file.seek를 이용하여 파일을 쪼개어 병렬로 처리 하는게 가장 빠른 방식 같다.

참고 문서

  • File Line Offset save
  • File random line Read
  • Pool.map - Multiple arguments
  • Python converting args to list


python Share Tweet +1