2009-11-23 4 views
1

저는 Python을 처음 접했고 누군가가 비교적 간단한 Perl 스크립트의 예를 Python으로 친절하게 변환 할 수 있는지 알고 싶습니다.Perl 스크립트를 Python으로 변환 : 해시 키 기반의 파일 2 개를 중복 제거

스크립트는 2 개의 파일을 사용하고 해시 키를 비교하여 두 번째 파일에서 고유 한 줄만 출력합니다. 또한 파일에 중복 행을 출력합니다. 필자는이 중복 제거 방법이 Perl과 매우 빠르며 Python이 어떻게 비교되는지보고 싶습니다.

#! /usr/bin/perl 

## Compare file1 and file2 and output only the unique lines from file2. 

## Opening file1.txt and store the data in a hash. 
open my $file1, '<', "file1.txt" or die $!; 
while (<$file1>) { 
    my $name = $_; 
    $file1hash{$name}=$_; 
} 
## Opening file2.txt and store the data in a hash. 
open my $file2, '<', "file2.txt" or die $!; 

while (<$file2>) { 
    $name = $_; 
    $file2hash{$name}=$_; 
} 

open my $dfh, '>', "duplicate.txt"; 

## Compare the keys and remove the duplicate one in the file2 hash 
foreach (keys %file1hash) { 
    if (exists ($file2hash{$_})) 
    { 
    print $dfh $file2hash{$_}; 
    delete $file2hash{$_}; 
    } 
} 

open my $ofh, '>', "file2_clean.txt"; 
print $ofh values(%file2hash) ; 

필자는 perl과 python 스크립트를 1 백만 라인이 넘는 2 개의 파일에서 테스트했으며 총 시간은 6 초 미만이었습니다. 이 사업 목적을 위해 성능이 뛰어납니다!

나는 크리스가 제공하는 스크립트를 수정하고 난 둘 다 결과에 매우 행복하다 : 1) 내가 스크립트를 수정있는 스크립트의 성능과 2) 용이성을 더 유연하게 :

#!/usr/bin/env python 

import os 

filename1 = raw_input("What is the first file name to compare? ") 
filename2 = raw_input("What is the second file name to compare? ") 

file1set = set([line for line in file(filename1)]) 
file2set = set([line for line in file(filename2)]) 

for name, results in [ 
    (os.path.abspath(os.getcwd()) + "/duplicate.txt", file1set.intersection(file2set)), 
    (os.path.abspath(os.getcwd()) + "/" + filename2 + "_clean.txt", file2set.difference(file1set))]: 
    with file(name, 'w') as fh: 
     for line in results: 
      fh.write(line) 
+2

여러분이 알고있는 것과 질문이 무엇인지 알 수 있도록 여러분의 파이썬 노력을 게시하십시오. –

답변

7

당신에게 주문에 신경 쓰지 않는다면 파이썬에서 세트를 사용할 수 있습니다 :

file1=set(open("file1").readlines()) 
file2=set(open("file2").readlines()) 
intersection = file1 & file2 #common lines 
non_intersection = file2 - file1 #uncommon lines (in file2 but not file1) 
for items in intersection: 
    print items 
for nitems in non_intersection: 
    print nitems 

다른 방법으로는 difflib, filecmp 라이브러리를 사용할 수 있습니다.

다른 방법으로는 목록 비교 만 사용하십시오.

# lines in file2 common with file1 
data1=map(str.rstrip,open("file1").readlines()) 
for line in open("file2"): 
    line=line.rstrip() 
    if line in data1: 
     print line 

# lines in file2 not in file1, use "not" 
data1=map(str.rstrip,open("file1").readlines()) 
for line in open("file2"): 
    line=line.rstrip() 
    if not line in data1: 
     print line 
+1

+1 짧고, 간결하며, 파이썬 적이며, 포인트가 –

+0

read(). split() 대신 readlines()를 사용하는 것이 더 좋습니다. file2의 고유 한 줄은 file2-file1 (차이 설정)입니다. 사용 | 세트 조합으로 두 파일의 모든 라인을 하나의 세트로 만듭니다. – gimel

+1

감사하지만이 스크립트는 각 파일의 전체 줄의 무결성을 보존하지 않습니다. 공백에 줄을 긋고 개별 단어를 출력합니다. – galaxywatcher

3

다음은 파일이 매우 큰 경우 약간 더 메모리가 많은 해결책입니다. 당신이 공백과 끝을 후행 포함 할 경우,

with open("file1.txt", "r") as file1: 
    file1set = set(line.rstrip() for line in file1) 

with open("file2.txt", "r") as file2: 
    with open("duplicate.txt", "w") as dfh: 
     with open("file2_clean.txt", "w") as ofh: 
      for line in file2: 
       if line.rstrip() in file1set: 
        dfh.write(line)  # duplicate line 
       else: 
        ofh.write(line)  # not duplicate 

참고 :이은 (한 번에 메모리에 파일 2 모두를 할 필요가있을 것 같지 않는 한) 원본 파일의 집합을 생성 측면도 및 인라인 비교의 문자가, 당신은 단지 line와 두 번째 line.rstrip()을 대체 할 수 있으며, 두 번째 라인을 단순화 : 파이썬 3.1로,

file1set = set(file1) 

또한을은 with 문은 세 with 문 그래서 여러 항목을 수 있습니다 하나에 결합 될 수 있습니다.

4

또 다른 변형 (다른 제안에서 단순히 구문 변경, 거기에 파이썬을 사용하여 그것을 할 여러 가지 방법이 있습니다).

file1set = set([line for line in file("file1.txt")]) 
file2set = set([line for line in file("file2.txt")]) 

for name, results in [ 
    ("duplicate.txt", file1set.intersection(file2set)), 
    ("file2_clean.txt", file2set.difference(file1set))]: 
    with file(name, 'w') as fh: 
     for line in results: 
      fh.write(line) 

사이드 노트 : 우리는 또 다른 perl 버전을 제안해야합니다. 매우 perlish가 아닌 버전에서 제안 된 것 ... 아래는 필자의 python 버전과 동등한 perl입니다. 처음 것 같이 보이지 않는다. 내가 지적하고자하는 것은 제안 된 응답 문제는 perl 대 python만큼 많은 알고리즘 및 언어 독립적이라는 것입니다.

use strict; 

open my $file1, '<', "file1.txt" or die $!; 
my %file1hash = map { $_ => 1 } <$file1>; 

open my $file2, '<', "file2.txt" or die $!; 
my %file2hash = map { $_ => 1 } <$file2>; 

for (["duplicate.txt", [grep $file1hash{$_}, keys(%file2hash)]], 
    ["file2_clean.txt", [grep !$file1hash{$_}, keys(%file2hash)]]){ 
    my ($name, $results) = @$_; 
    open my $fh, ">$name" or die $!; 
    print $fh @$results; 
} 
+0

감사합니다. 필자는이 파일을 2 개의 큰 파일로 테스트했으며 펄 스크립트처럼 작동합니다. 속도는 타오른다. 문자 그대로 3 초 동안 두 파일을 비교합니다. 파일 1에 150,000 개의 레코드가 있고 파일 2에는 200,000 개의 레코드가 있습니다. 2 개의 스크립트를 보면 Python은 훨씬 더 깨끗해 보입니다. – galaxywatcher

+0

테스트 파일에 perl 버전을 사용하는 퍼포먼스는 무엇입니까? 필자의 perl 버전을 쉽게 최적화 할 수 있습니다 (분명히 file2hash에서 동일한 루프를 두 번 수행합니다). 그러나 대부분의 시간이 IO에서 소요되므로 버전간에 많은 차이가 없어야합니다. – kriss

+0

$ 신의 사랑을 위해 open과 같은 시스템 호출의 반환 값을 확인하십시오. 또는 자동 검색을 사용하려면 상단에 "자동 검색"을 사용하십시오. 또한 "strict"를 사용하고 변수를 선언하십시오. 예제 코드를 짧게하고 싶었지만 능숙하지 않은 프로그래머는 복사하여 붙여 넣을 수 있다고 생각합니다. 최적화에 관해서는 물론 이중 루프를 잃을 수도 있지만 map 및 grep에 대한 여분의 {} 블록을 제거하고 루핑 대신 전체 @ $ results 배열을 한 번에 인쇄 할 수 있습니다. 어느 쪽이든간에 나는 어쨌든 일이 IO 기반 일 가능성이 높다는 것에 동의한다. – tsee