2010-07-29 4 views
2

여러 CSV 파일에서 라인을 일치하고 특정 필드를 병합 약 20 CSV의 모두가 같이 있음 :내가 가진

"[email]","[fname]","[lname]","[prefix]","[suffix]","[fax]","[phone]","[business]","[address1]","[address2]","[city]","[state]","[zip]","[setdate]","[email_type]","[start_code]" 
내가 생성 할 필요가 말했다 똑같은 일이 있었어요 무엇

만에 각 파일에는 이제 전자 메일이 일치하는 다른 모든 파일의 start_code가 포함됩니다.

다른 필드 중 하나라도 일치하지 않으면 전자 메일 필드 만 중요하며 각 파일의 유일한 변경 사항은 전자 메일과 일치하는 다른 파일의 다른 start_code 값을 추가하는 것입니다.

"[email protected]","anon",,,,,,,,,,,,01/16/08 08:05 PM,,"WIQC PDX" 
"[email protected]","anon",,,,,,,,,,,,01/16/08 08:05 PM,,"OOTA" 
"[email protected]","anon",,,,,,,,,,,,01/16/08 08:05 PM,,"ITOS" 

"[email protected]","anon",,,,,,,,,,,,01/16/08 08:05 PM,,"WIQC PDX, OOTA, ITOS" 

에 : 같은 이메일 oota.csv의 wicq.csv 등장하고, 경우

예를 들어,이되는 것을 각 파일에 다음과 갈 것 itos.csv 내가 난 펄 것처럼뿐만 아니라 OS X의 명령 행 (AWK, 나오지도 등) 될 것이다 가능한 모든 세 개의 파일 (wicq.csv, oota.csv 및 itos.csv)

도구에 대한

너무 익숙하지 않은 경우도 있습니다. 이것을하는 더 좋은 방법이 될 수 있습니다.

+0

그래서 그 수정'WIQC, PDX, 오타는 ITOS'이 세 CSV 파일의 모든 사람에 밀어 것? – Anders

+0

@ Anders, 네. (WICQ PDX는 귀하의 의견에 언급 된 두 가지가 아닌 하나의 수정입니다.) – alex

답변

1
use strict; 
use warnings; 
use Text::CSV_XS; 

# Supply csv files as command line arguments. 
my @csv_files = @ARGV; 
my $parser = Text::CSV_XS->new; 

# In my test data, the email is the first field. The field 
# to be merged is the second. Adjust accordingly. 
my $EMAIL_i = 0; 
my $MERGE_i = 1; 

# Process all files, creating a set of key-value pairs: 
# $sc{EMAIL} = [ LIST OF VALUES OBSERVED IN THE MERGE FIELD ] 
my %sc; 
for my $cf (@csv_files){ 
    open(my $fh_in, '<', $cf) or die $!; 

    while (my $line = <$fh_in>){ 
     die "Failed parse : $cf : $.\n" unless $parser->parse($line); 
     my @fields = $parser->fields; 
     push @{ $sc{$fields[$EMAIL_i]} }, $fields[$MERGE_i]; 
    } 
} 

# Process the files again, writing new output. 
for my $cf (@csv_files){ 
    open(my $fh_in, '<', $cf)    or die $!; 
    open(my $fh_out, '>', "${cf}_new.csv") or die $!; 

    while (my $line = <$fh_in>){ 
     die "Failed parse : $cf : $.\n" unless $parser->parse($line); 
     my @fields = $parser->fields; 

     $fields[$MERGE_i] = join ', ', @{ $sc{$fields[$EMAIL_i]} }; 

     $parser->print($fh_out, \@fields); 
     print $fh_out "\n"; 
    } 
} 
+0

아주 잘 돌아갔습니다! 나는 "binmode $ fh_in"을 넣어야했다 : "utf8"; " 그리고 수동으로 각 파일 (: g/^ $/d)에서 빈 줄을 지우지 만 제대로 작동합니다. 감사. – alex

0

나는의 라인을 따라 뭔가를 수행하여이 문제를 접근하는 것입니다 :

cut -d ',' -f1,16 *.csv | 
    sort | 
    awk -F, '{d=""; if (array[$1]) d=","; array[$1] = array[$1] d $2} END { for (i in array) print i "," array[i]}' | 
    while IFS="," read -r email start; do sed -i "/^$email,/ s/,[^,]*\$/,$start/" *.csv; done 

이것은 모든 이메일 (cut/sort) 및 start_codes 통합합니다 (awk) 그 목록을 만듭니다. 그런 다음 각 파일 (while)에서 일치하는 전자 메일의 start_code를 (sed)으로 바꿉니다.

그러나 나는보다 효율적인 방법이 있어야한다고 생각합니다.

+0

대문자로 시작하는 모든 문자의 이름을 대문자로 바꾸 었습니다. "sed : 1 :"R2R.csv ": 잘못된 명령 코드 R" 이제이 오류가 발생합니다. : "sed : 1 :"bwtl.csv ": 정의되지 않은 레이블 'wtl.csv'" 같은 초기 문제의 결과로 sed가 파일 이름을 명령으로 사용하고 있다고 생각합니다. – alex

+0

@alex : 별표 앞에 공백을 두지 않았는지 또는 따옴표가 잘못 위치했는지 확인하려면 두 번 검사하십시오. GNU 기반 (예 : Linux) 시스템에 있습니까? 파일에 슬래시가 있습니까? 'sed' 명령의 구분 기호를 파이프 (''s ​​| old | new | '') 나 데이터에없는 다른 문자로 변경해보십시오. –

0

다음은 필요한 펄을 제공하는 간단한 펄 프로그램입니다. 사전에 정렬된다는 사실에 의존하여 입력에 대해 단일 패스를 수행합니다.

전자 메일이 변경되지 않으므로 줄을 읽고 코드를 추가합니다. 전자 메일이 변경되면 레코드를 인쇄하고 코드 필드에 여분의 큰 따옴표를 수정합니다.

#!/usr/bin/perl -l 

use strict; 
use warnings; 

my $last_email = undef; 
my @current_record =(); 
my @fields =(); 

sub print_record { 
    # Remove repeated double quotes introduced when we appended the code 
    $current_record[15] =~ s/""/, /g; 
    print join ",", @current_record; 
    @current_record =(); 
} 

while (my $input_line = <>) { 
    chomp $input_line; 
    @fields = split ",", $input_line; 

    # Print a record when the email we read changes. Avoid printing on the first 
    # loop by checking we have read at least one email ($last_email is defined). 
    defined $last_email && ($fields[0] ne $last_email) && print_record; 

    if ([email protected]_record) { 
    # We are starting to process a new email. Grab all fields. 
    @current_record = @fields; 
    } 
    else { 
    # We have consecutive records with the same email. Append the code. 
    $current_record[15] .= $fields[15]; 
    } 

    # Remember the last processed email. When it changes we will print @current_record. 
    $last_email = $fields[0]; 
} 

# Print the last record 
print_record 

-l 스위치는 자동으로 새 줄 문자 (os가 무엇이든간에)를 추가합니다. 이 같은

전화를 :

sort *.csv | ./script.pl