Como dividir um arquivo de texto grande em arquivos menores com igual número de linhas?

515

Eu tenho um grande arquivo de texto simples (por número de linhas) que gostaria de dividir em arquivos menores, também por número de linhas. Portanto, se meu arquivo tiver cerca de 2 milhões de linhas, gostaria de dividi-lo em 10 arquivos que contêm 200k linhas ou 100 arquivos que contêm 20k linhas (mais um arquivo com o restante; ser igualmente divisível não importa).

Eu poderia fazer isso facilmente no Python, mas gostaria de saber se existe algum tipo de maneira ninja de fazer isso usando utilitários bash e unix (em oposição a loop manual e linhas de contagem / particionamento).

danben
fonte
2
Por curiosidade, depois que eles são "divididos", como alguém os "combina"? Algo como "cat part2 >> part1"? Ou existe outro utilitário ninja? mente atualizando sua pergunta?
dlamotte
7
Para colocá-lo novamente em conjunto,cat part* > original
Mark Byers
9
sim, gato é uma abreviação de concatenar. Em geral, o apropos é útil para encontrar comandos apropriados. IE ver a saída de: apropos split
pixelbeat
@pixelbeat Isso é muito legal, graças
danben
3
Além disso, os usuários do OS X devem garantir que o arquivo contenha quebras de linha no estilo LINUX ou UNIX / indicadores de fim de linha (LF) em vez de MAC OS X - indicadores de fim de linha (CR) no estilo - a divisão e Os comandos csplit não funcionarão se as quebras semelhantes forem Retorno de carro em vez de LineFeeds. O TextWrangler do software BareBones pode ajudá-lo se você estiver no Mac OS. Você pode escolher a aparência dos caracteres de quebra de linha. quando você salva (ou Salvar como ...) seus arquivos de texto.

Respostas:

856

Você já olhou para o comando split?

$ split --help
Usage: split [OPTION] [INPUT [PREFIX]]
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default
size is 1000 lines, and default PREFIX is `x'.  With no INPUT, or when INPUT
is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -a, --suffix-length=N   use suffixes of length N (default 2)
  -b, --bytes=SIZE        put SIZE bytes per output file
  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file
  -d, --numeric-suffixes  use numeric suffixes instead of alphabetic
  -l, --lines=NUMBER      put NUMBER lines per output file
      --verbose           print a diagnostic to standard error just
                            before each output file is opened
      --help     display this help and exit
      --version  output version information and exit

Você poderia fazer algo assim:

split -l 200000 filename

que criará arquivos cada um com 200000 linhas nomeadas xaa xab xac...

Outra opção, dividida por tamanho do arquivo de saída (ainda divide em quebras de linha):

 split -C 20m --numeric-suffixes input_filename output_prefix

cria arquivos como output_prefix01 output_prefix02 output_prefix03 ...cada um com tamanho máximo de 20 megabytes.

Mark Byers
fonte
16
você também pode dividir um arquivo por tamanho: split -b 200m filename(m para megabytes, k para kilobytes ou nenhum sufixo de bytes)
Abhi Beckert
136
divididos por tamanho e garantir que os arquivos são divididos em quebras de linha: split -C 200m filename
Clayton Stanley
2
split produz saída ilegível com entrada Unicode (UTF-16). Pelo menos no Windows com a versão que tenho.
Vertigo
4
@geotheory, siga as orientações de LeberMac, no início do tópico, sobre como converter as terminações de linha CR (Mac) em finais de linha LR (Linux) usando TextWrangler ou BBEdit. Eu tive exatamente o mesmo problema que você até encontrar esse conselho.
Sstringer
6
-dopção não está disponível no OSX, use em seu gsplitlugar. Espero que isso seja útil para usuários de Mac.
user5698801
80

E o comando split ?

split -l 200000 mybigfile.txt
Robert Christie
fonte
39

Sim, existe um splitcomando. Ele dividirá um arquivo por linhas ou bytes.

$ split --help
Usage: split [OPTION]... [INPUT [PREFIX]]
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default
size is 1000 lines, and default PREFIX is `x'.  With no INPUT, or when INPUT
is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -a, --suffix-length=N   use suffixes of length N (default 2)
  -b, --bytes=SIZE        put SIZE bytes per output file
  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file
  -d, --numeric-suffixes  use numeric suffixes instead of alphabetic
  -l, --lines=NUMBER      put NUMBER lines per output file
      --verbose           print a diagnostic just before each
                            output file is opened
      --help     display this help and exit
      --version  output version information and exit

SIZE may have a multiplier suffix:
b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,
GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.
Dave Kirby
fonte
Tentei georgec @ ATGIS25 ~ $ split -l 100000 /cygdrive/P/2012/Job_044_DM_Radio_Propogation/Working/FinalPropogation/TRC_Longlands/trc_longlands.txt mas não há arquivos divididos no diretório - onde está a saída?
8133 GeorgeC
1
Ele deve estar no mesmo diretório. Por exemplo, se eu quiser dividir por 1.000.000 de linhas por arquivo, faça o seguinte: split -l 1000000 train_file train_file.e no mesmo diretório, receberei train_file.aao primeiro milhão, depois trail_file.abo próximo milhão, etc.
Será
1
@GeorgeC e você pode obter diretórios de saída personalizado com o prefixo: split input my/dir/.
Ciro Santilli escreveu:
15

usar split

Dividir um arquivo em partes de tamanho fixo, cria arquivos de saída contendo seções consecutivas de INPUT (entrada padrão se nenhuma for fornecida ou INPUT for `- ')

Syntax split [options] [INPUT [PREFIX]]

http://ss64.com/bash/split.html

zmbush
fonte
13

Usar:

sed -n '1,100p' filename > output.txt

Aqui, 1 e 100 são os números de linha nos quais você capturará output.txt.

Harshwardhan
fonte
Isso só obtém as primeiras 100 linhas, você precisa fazer um loop para dividir sucessivamente o arquivo nas próximas 101..200 etc. Ou apenas usar splitcomo todas as principais respostas aqui já lhe dizem.
Tripleee
10

divida o arquivo "file.txt" em 10000 arquivos de linhas:

split -l 10000 file.txt
ialqwaiz
fonte
9

split(do GNU coreutils, desde a versão 8.8 de 22-12-2010 ) inclui o seguinte parâmetro:

-n, --number=CHUNKS     generate CHUNKS output files; see explanation below

CHUNKS may be:
  N       split into N files based on size of input
  K/N     output Kth of N to stdout
  l/N     split into N files without splitting lines/records
  l/K/N   output Kth of N to stdout without splitting lines/records
  r/N     like 'l' but use round robin distribution
  r/K/N   likewise but only output Kth of N to stdout

Assim, split -n 4 input output.irá gerar quatro arquivos ( output.a{a,b,c,d}) com a mesma quantidade de bytes, mas as linhas podem ser quebradas no meio.

Se queremos preservar linhas completas (isto é, dividir por linhas), isso deve funcionar:

split -n l/4 input output.

Resposta relacionada: https://stackoverflow.com/a/19031247

Denilson Sá Maia
fonte
9

Caso você queira apenas dividir por x número de linhas em cada arquivo, as respostas fornecidas splitsão válidas. Mas estou curioso para saber que ninguém prestou atenção nos requisitos:

  • "sem precisar contá-los" -> usando wc + cut
  • "tendo o restante em arquivo extra" -> split faz por padrão

Não posso fazer isso sem "wc + cut", mas estou usando isso:

split -l  $(expr `wc $filename | cut -d ' ' -f3` / $chunks) $filename

Isso pode ser facilmente adicionado às suas funções bashrc para que você possa invocá-lo passando o nome do arquivo e os pedaços:

 split -l  $(expr `wc $1 | cut -d ' ' -f3` / $2) $1

Caso você queira apenas x pedaços sem restante no arquivo extra, basta adaptar a fórmula para somar (pedaços - 1) em cada arquivo. Eu uso essa abordagem, porque geralmente eu só quero x número de arquivos em vez de x linhas por arquivo:

split -l  $(expr `wc $1 | cut -d ' ' -f3` / $2 + `expr $2 - 1`) $1

Você pode adicionar isso a um script e chamá-lo de "jeito ninja", porque se nada atender às suas necessidades, você poderá construí-lo :-)

m3nda
fonte
Ou, apenas use a -nopção de split.
Amit Naidu
8

você também pode usar o awk

awk -vc=1 'NR%200000==0{++c}{print $0 > c".txt"}' largefile
ghostdog74
fonte
3
awk -v lines=200000 -v fmt="%d.txt" '{print>sprintf(fmt,1+int((NR-1)/lines))}'
Mark Edgar
0

O HDFS obtém um arquivo pequeno e é derramado no tamanho da propriedade.

Este método causará quebra de linha

split -b 125m compact.file -d -a 3 compact_prefix

Eu tento ficar imenso e dividir em cerca de 128 MB cada arquivo.

# split into 128m ,judge sizeunit is M or G ,please test before use.

begainsize=`hdfs dfs -du -s -h /externaldata/$table_name/$date/ | awk '{ print $1}' `
sizeunit=`hdfs dfs -du -s -h /externaldata/$table_name/$date/ | awk '{ print $2}' `
if [ $sizeunit = "G" ];then
    res=$(printf "%.f" `echo "scale=5;$begainsize*8 "|bc`)
else
    res=$(printf "%.f" `echo "scale=5;$begainsize/128 "|bc`)  # celling ref http://blog.csdn.net/naiveloafer/article/details/8783518
fi
echo $res
# split into $res files with number suffix.  ref  http://blog.csdn.net/microzone/article/details/52839598
compact_file_name=$compact_file"_"
echo "compact_file_name :"$compact_file_name
split -n l/$res $basedir/$compact_file -d -a 3 $basedir/${compact_file_name}
Matiji66
fonte