Perl을 사용하여 대용량 CSV 파일에서 특정 CSV 열 및 중복 행 제거

Perl을 사용하여 대용량 CSV 파일에서 특정 CSV 열 및 중복 행 제거

대용량 CSV 파일(300MB 이상)이 있고 2,3, 6-8열만 제거하고 Perl을 사용하여 중복 행을 제거하고 싶습니다.

참고 1: 모든 열은 ,(쉼표)로 구분되지만 때로는 내 셀 값에 ,(마지막 행, 열 9 및 10 참조)로 구분된 하나 이상이 포함되어 ,있으므로 "여전히 csv 파일을 처리할 수 있기를 원합니다. , ,셀 내부에 있는 경우에도 마찬가지입니다 .

참고 2: input.csv 및 output.csv 파일에 대한 링크를 추가했습니다.

입력.csv

Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
info 1,info 2,info 3,...,info 10
address 1,address 2,....,address 10
city 1,city 2,city 3,city 4,city 5,city 6,city 7,city 8,"city 9, extra","city 10, new"

출력.csv

Col1,Col4,Col5,Col9,Col10
info 1,info 4,info 5,info 9,info 10
address 1,address 4,address 5,address 9,address 10
city 1,city 4,city 5,"city 9, extra","city 10, new"

정규식을 사용하여 마지막 열을 제거하는 Perl 명령을 찾았지만 이것이 충분한지 또는 내 상황에 맞게 조정하는 방법을 모르겠습니다(다른 제안은 매우 환영합니다!).

perl -pe 's/.*\K,.*//'

Perl을 사용하여 2,3, 6-8열만 제거하고 중복 행을 제거하는 것이 가능합니까?

추신: 중복 행을 포함하도록 input.csv 파일을 업데이트했습니다.

감사합니다!

답변1

가장 간단한 방법은밀러일명 mlr, CSV, json 및 기타 입력 또는 출력 형식의 데이터 작업을 위한 훌륭한 도구입니다. 예를 들어:

$ mlr --csv --implicit-csv-header --headerless-csv-output \
    cut -x -f 2,3,6,7,8 \
    then uniq -a input.csv  
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10

--implicit-csv-header및 옵션을 함께 사용하면 --headerless-csv-output헤더 행이 효과적으로 무시되고(즉, 다른 데이터 행과 동일하게 만들어짐) 이름이 아닌 숫자로 잘라낼 필드를 지정할 수 있습니다.

누락된 필드에 일부 정크 데이터를 추가하려면 샘플 input.csv 파일을 편집해야 했습니다. mlr그렇지 않으면 불평할 것입니다. 또한 중복 제거가 작동하는지 테스트하기 위해 중복 입력 행을 추가했습니다.

$ cat input.csv 
Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
info 1,info 2,info 3,info 4,5,6,7,8,9,info 10
info 1,info 2,info 3,info 4,5,6,7,8,9,info 10
address 1,address 2,3,4,5,6,7,8,9,address 10
city 1, city 2,3,4,5,6,7,8,9,city 10

Perl로 하고 싶다면:

  1. 간단한 쉼표로 구분된 입력만 처리해야 하는 경우:
$ perl -F, -lane '
  next if $seen{$_}++;
  splice @F,5,3;
  splice @F,1,2;
  print join ",", @F' input.csv
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10

이는 Perl의 -a옵션을 사용하여 각 입력 행을 이라는 배열로 자동 분할합니다 @F. 이 -F옵션은 사용할 구분 기호를 알려줍니다.

참고 1: Perl 배열은 1이 아닌 0부터 시작합니다. 따라서 배열 요소 5는 열 6입니다. splice @$row, 5, 3배열에서 요소 5(즉, 열 6, 7, 8)부터 시작하여 세 개의 요소를 삭제합니다. perldoc -f splice자세히보다.

참고 2: 여기서는 역순으로 열을 제거합니다(즉, 번호가 높은 열앞으로낮은 숫자). 그렇지 않고 열 5, 6, 7을 삭제하기 전에 열 2와 3을 삭제하면 첫 번째 삭제로 인해 해당 열의 번호가 다시 매겨집니다(3, 4, 5로).

  1. 사용텍스트::CSV유효한 CSV(쉼표가 포함된 여러 줄로 묶인 따옴표 열 등 포함)를 처리합니다.
$ perl -MText::CSV -e '
  my $csv = Text::CSV->new();
  while (my $row = $csv->getline(*ARGV)) {
    next if $seen{join ",", @$row}++;
    splice @$row, 5, 3;
    splice @$row, 1, 2;
    $csv->say(*STDOUT, $row);
  }' input.csv
Col1,Col4,Col5,Col9,Col10
"info 1","info 4",5,9,"info 10"
"address 1",4,5,9,"address 10"
"city 1",4,5,9,"city 10"

여기서 주목할 만한 네 가지 사항은 다음과 같습니다.

  1. Text::CSV핵심 Perl 모듈이 아니므로 설치가 필요합니다. 전부는 아니지만 대부분의 Linux 배포판에서 작동합니다. 예를 들어 Debian에서는 sudo apt-get install libtext-csv-perl. 그렇지 않은 경우에는 cpanPerl과 함께 제공되는 명령을 사용하여 설치할 수 있습니다 .

  2. Text::CSV의 메서드 getline()(위에 표시 $row = $csv->getline(*ARGV))는 배열 또는 arrayref에 대한 참조를 반환합니다. 이는 전체 배열을 가리키는 스칼라 값입니다( 자세한 내용을 확인 man perlref하고 man perldata알아보세요).

  3. $row위의 코드에는 arrayref가 포함되어 있습니다. $row 사용/조작은 참조하는 데이터가 아닌 참조 자체에 적용됩니다. 따라서 예를 들어 $row2 = $row데이터가 아닌 참조가 복사됩니다. 두 참조 모두 동일한 데이터를 가리킵니다. @$rowarrayref를 배열로 "역참조"하여 다른 배열처럼 사용할 수 있습니다.

  4. *ARGVin은 명령줄에 제공된 모든 파일 이름 인수에서 입력을 읽는 특수 파일 핸들입니다 getline(*ARGV)(이러한 인수는 Perl에서 @ARGV라는 배열에 저장됩니다). 파일 이름이 아닌 인수(예: 스크립트에 옵션을 처리하는 코드가 있는 경우 옵션)는 처리되어 @ARGV에서 제거된 것으로 가정됩니다. 존재하지 않거나 열 수 없는 파일 이름(예: 권한으로 인해)은 오류 메시지를 생성합니다. 즉, 사용자가 지정한 하나 이상의 파일 이름을 읽습니다. 인수는 -표준 입력으로 처리되므로 파일, 표준 입력 또는 둘 다에서 입력을 읽을 수 있습니다.

이것은 Text::CSV의 기능과 사용 방법에 대한 매우 간단하고 원시적인 예입니다. 자세한 내용과 예제를 보려면 매뉴얼 페이지를 읽어보세요.

위의 출력 예에서 볼 수 있듯이 기본적으로 Text::CSV는 공백이 포함된 텍스트 필드를 인용합니다. 이를 원하지 않으면 속성을 quote_space0으로 설정하거나 new다음을 사용하여 $csv 개체를 생성하여 재정의할 수 있습니다.

my $csv = Text::CSV->new({ quote_space => 0 });

또는 그 이후:

my $csv = Text::CSV->new();
$csv->quote_space(0);

그러면 출력은 다음과 같습니다.

Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10

답변2

배열로 변환하고, 생각해 보고, csv로 다시 생성하세요.

perl -pe '@c = split(","); splice(@c, 1, 2); splice(@c, 3, 3); $_ = join(", ", @c)

귀하의 필드가 참조된 경우 다음을 사용할 수 있습니다 Text::CSV.

$ cat in.csv 
Col1,Col2,Col3,Col4,Col5
one,two,three,four,five
six,"se,ven","ei,ght",nine,ten
$ perl -MText::CSV -e 'Text::CSV::csv( in => "in.csv", headers => false, on_in => sub { splice( @{@_[1]}, 1, 2) } )'
Col1,Col4,Col5
one,four,five
six,nine,ten

Perl에 대해 질문했지만 인식을 높이기 위해 캡처 도구도 고려하십시오. cut -f '1,4,5,9,10' -d ,

관련 정보