CSV 파일을 초기 열(헤더 포함)별로 분할하는 방법은 무엇입니까?

CSV 파일을 초기 열(헤더 포함)별로 분할하는 방법은 무엇입니까?

이것은 다른 두 가지 질문의 조합입니다(한 줄에 접두사로 파일을 분할하는 방법그리고헤더를 포함하여 열을 기준으로 파일을 분할하는 방법). 나는 다음과 같이 시작하고 싶습니다 input.csv:

id,first,second,third
1,a,b,c
333,b,b,b
1,d,e,f
2,d,e,f
1,c,d,e
333,a,a,a
[more lines in the same format]

이 내용에 1.csv:

id,first,second,third
1,a,b,c
1,d,e,f
1,c,d,e

, 이 콘텐츠는 다음 위치에 있습니다 2.csv.

id,first,second,third
2,d,e,f

, 이 333.csv:

id,first,second,third
333,b,b,b
333,a,a,a

, 그건:

  1. 모든 ID를 다음으로 변경하세요.질소입력하다 N.csv.
  2. 행을 순서대로 유지원본처럼.
  3. 제목 포함모든 출력 파일의 원시 파일.

이 또한 매우 빨라야 하므로 while read루프는 다음과 같습니다.아니요잘라버릴 거예요.

답변1

이 GNU awk 명령은 이 문제를 해결할 수 있습니다.

awk -F ',' 'NR==1{h=$0; next};!seen[$1]++{f=$1".csv"; print h > f};{f=$1".csv"; print >> f; close(f)}' input.csv

경고: 첫 번째 필드에 이스케이프된 쉼표가 있으면 작동하지 않습니다. 다른 분야의 쉼표는 정상적으로 작동합니다.

설명하다:

  • -F ','(필드 구분 기호) $1등이 공백으로 구분된 값이 아닌 CSV 열을 참조하는지 확인합니다.
  • NR==1{h=$0; next}NR==1전체 헤더 행을 변수 h( )에 저장하고 해당 행( )을 건너뛰어 h=$0첫 번째 행( )을 특별히 처리합니다 next.
  • !seen[$1]++{f=$1".csv"; print h > f}$1특수 문자( ) 가 처음 발생하면 다음 항목을 filename 변수에 저장하고 헤더를 해당 파일( )에 저장하여 처리됩니다 .!seen[$1]$1.csvfprint h > f
  • {f=$1".csv"; print >> f; close(f)}현재 행을 파일에 추가하고( print >> f) 파일 설명자를 닫아( close(f)) 특정 ID를 가진 모든 행이 처리된 후에 해당 행이 유지되지 않도록 합니다.

보너스: 다른 필드로 바꾸면 $1예상대로 작동해야 합니다. 즉, 주어진 열에 해당 값이 포함된 행이 포함된 해당 열의 각 고유 값에 대한 파일을 생성해야 합니다.

답변2

(다른 답변으로 모든 사람에게 스팸을 보내서 죄송합니다.) 많은 상황에서 제공된 우아한 awk 버전이 완벽합니다. 하지만 재치 있는 말 이상의 삶이 있으며 우리에게는 종종 더 많은 것이 필요합니다.

  • 복잡한 csv 파일을 처리하는 추가 코드를 추가합니다.
  • 추가 정규화, 형식 재지정, 처리 단계를 추가합니다.

다음 프레임워크에서는 CSV 파일에 대한 파서를 사용합니다. 이번에는 정수 사용을 피하고 심지어 변수를 엄격하게 선언했습니다!

#!/usr/bin/perl

use strict;
use Parse::CSV;
my %dict=();

my $c = Parse::CSV->new(file => 'a1.csv');

while ( my $row = $c->fetch ) {                    ## for all records
   $dict{$row->[0]} .=   join(" :: ",@$row)."\n";  ## process and save
}

for my $k (keys %dict){                            ## create the cvs files
   open(F,">","$k.cvs") or die;
   print F $dict{$k};
   close F;
}
  • 가장 큰 장점은 더 복잡한 csv 파일을 처리할 수 있다는 것입니다. 이번에는 csv 입력에 ";"이 포함된 문자열이 포함될 수 있으며 여러 줄 필드가 포함될 수 있습니다(csv 사양은 복잡합니다!).
 1111,2,3
 "3,3,3",a,"b, c, and d"
 "a more, complex
        multiline record",3,4
  • 처리 단계를 설명하기 위해 필드 구분 기호가 ":"로 변경되었습니다.
  • 추가 단계를 설명하기 위해 몇 가지 최적화를 추가했습니다. 사전 캐싱을 사용했기 때문에 이 스크립트는 다른 솔루션보다 100배 빠르게 실행되었습니다.

답변3

이것은 답변이 아니라 IObO의 탁월한 답변의 스크롤 방지 변형입니다 ...

awk -F, 'NR==1{h=$0; next} {print seen[$1]++ ? $0 : h "\n" $0 >$1 ".csv"}'

답변4

파이프만 사용하고 다음을 사용하지 않는 구식 버전 awk:

경고하다:위의 솔루션보다 평균적으로 느리게 실행되며 awk속도는 입력 파일의 키 수에 따라 달라집니다.

cut -d , -f 1 input.csv | fgrep -v id | sort | uniq | xargs -n1 sh -c '(head -n1 input.csv && egrep "^${0}," input.csv) > ${0}.csv'

그것이 하는 일은:

  • cut -d , -f 1 input.csv파일의 각 줄을 문자별로 나누고 ,첫 번째 열( -f 1)을 가져와 키만 유지합니다.
  • fgrep -v id제목 건너뛰기
  • sort | uniq각 키 중 하나만 정렬하여 보관하세요.
  • xargs -n1 sh -c '<sub shell>'각 키에 대해 서브셸 실행
  • head -n1 input.csv서브셸의 첫 번째 부분은 입력 파일의 헤더를 가져옵니다.
  • 그런 다음 egrep "^${0}," input.csv키와 일치하는 행을 잡고명확하지 않을 수도 있지만 이는 행별로 반복되므로 속도가 느립니다.
  • 마지막으로 > ${0}.csv출력은 키 이름의 파일에 기록됩니다.

관련 정보