Precisa dividir um arquivo csv grande

2

Eu tenho esse arquivo csv que precisa ser dividido em arquivos menores. nenhum problema com split -l 20000 test.csv meu problema é que o arquivo contém cabeçalhos diferentes. quer dividir dizer a cada + 1000 linhas, mas ele precisa ser dividido após o cabeçalho do pagamento e o novo arquivo precisa começar com o cabeçalho do cliente.

cust header,xxx,xxxxxx,xxxxxx
txn header,xxxx,xxx,,xxxx,xxxxx,,xxx
detail header,xxxx,xxxx,xxxxxx,xxxx,xxxx
detail header,xxxxxxxx,xxxxxxxxxx,xxx,,
pay header,,,,,xxxx,xxxxx
cust header,xxx,xxxxxx,xxxxxx
txn header,xxxx,xxx,,xxxx,xxxxx,,xxx
detail header,xxxx,xxxx,xxxxxx,xxxx,xxxx
pay header,,,,,xxxx,xxxxx
cust header,xxx,xxxxxx,xxxxxx
txn header,xxxx,xxx,,xxxx,xxxxx,,xxx
detail header,xxxx,xxxx,xxxxxx,xxxx,xxxx
pay header,,,,,xxxx,xxxxx
    
por Daniel 15.06.2018 / 14:32

2 respostas

1

A solução do ilkkachu é engenhosa, usa um único executável e é provavelmente a resposta correta. No entanto, nunca consegui resumir os usos avançados de awk . Se o ilkkachu não tivesse respondido primeiro, eu poderia ter optado por csplit . csplit dividirá um arquivo de texto baseado em linhas de contexto (ok, expressões regulares). Você poderia então pegar essa saída e dividir ainda mais os arquivos com o utilitário split que você já conhece:

$ csplit --prefix="MySplit." test.csv '/^cust header,/' '{*}'
0
174
134
134

Essas são as contagens de bytes de cada parte (que ignoramos neste caso). Agora, iterar sobre cada MySplit e dividir ainda mais a sua exigência de 20k:

$ for i in MySplit.0*; do
    split --additional-suffix=".$i" -l 20000 "$i"
  done

Por exemplo, usando -l 2 em vez de 20k, o resultado final dado ao seu exemplo seria (classificado por extensão):

$ ls -lhXB
total 44K
-rw-r--r-- 1 hunteke hunteke   0 Jun 15 13:31 MySplit.00
-rw-r--r-- 1 hunteke hunteke 174 Jun 15 13:31 MySplit.01
-rw-r--r-- 1 hunteke hunteke  67 Jun 15 13:27 xaa.MySplit.01
-rw-r--r-- 1 hunteke hunteke  81 Jun 15 13:27 xab.MySplit.01
-rw-r--r-- 1 hunteke hunteke  26 Jun 15 13:27 xac.MySplit.01
-rw-r--r-- 1 hunteke hunteke 134 Jun 15 13:31 MySplit.02
-rw-r--r-- 1 hunteke hunteke  67 Jun 15 13:27 xaa.MySplit.02
-rw-r--r-- 1 hunteke hunteke  67 Jun 15 13:27 xab.MySplit.02
-rw-r--r-- 1 hunteke hunteke 134 Jun 15 13:31 MySplit.03
-rw-r--r-- 1 hunteke hunteke  67 Jun 15 13:27 xaa.MySplit.03
-rw-r--r-- 1 hunteke hunteke  67 Jun 15 13:27 xab.MySplit.03
-rw-r--r-- 2 hunteke hunteke 442 Jun 15 13:06 test.csv
    
por 15.06.2018 / 19:42
6

Você poderia fazer algo assim com awk :

awk -vfilename=output -vcut=1000  '
    BEGIN { nl=0; nf=1; f=filename "." nf;} 
    ++nl >= cut && /^cust header,/ {
         close(f); nl=0; f=filename "." ++nf}
   {print > f}' < file

Ele mantém uma contagem das linhas vistas e reabre um novo arquivo de saída se a contagem for maior que cut (1000 aqui) e a linha atual começar com cust header, . Os arquivos de saída são denominados output.1 , output.2 , ... (a variável filename )

    
por 15.06.2018 / 14:49