Dividir o fluxo de texto pelo valor de hash do primeiro campo na linha

0

Estou procurando um filtro rápido que pegue um arquivo de texto na entrada padrão, trace cada linha até o primeiro caractere de tabulação e coloque a linha em um arquivo diferente, dependendo do valor do hash (modulo algum número de arquivos) . Por exemplo, algo como:

$ cat > foo
a   1
b   2
c   3
d   4
^D
$ hashit -o bar -n2 < foo
$ cat bar.0
b   2
$ cat bar.1
a   1
c   3
d   4

A função hash deve ser consistente entre invocações.

Isto é um pouco como o utilitário padrão split , mas eu quero dividir o conteúdo em hash das linhas, não simplesmente o número de linhas por componente.

    
por Reid 20.05.2013 / 23:39

4 respostas

0

Tanto quanto eu posso dizer, não há utilitários padrão para fazer isso, e uma implementação ingênua no Python é muito lenta.

Então, eu implementei em C no projeto de código aberto onde eu precisava, QUAC . Espero que isso seja útil para os outros. (Eu ainda não forcei, mas isso deve acontecer dentro de alguns dias.)

    
por 22.05.2013 / 21:25
1

Então você precisa de velocidade. Esse tipo de velocidade provavelmente requer C (embora o Perl possa ser suficientemente otimizado). Infelizmente, o armazenamento em buffer é complexo para ser feito manualmente em C e é lento automaticamente em Perl / Python / Java.

Portanto, um caminho possível para uma solução C com menos dor, supondo que você possa executar em um sistema de 64 bits e não esteja processando mais do que alguns poucos TB de dados:

  1. abre os arquivos de saída
  2. mmap do arquivo de entrada inteiro
  3. lembre-se da posição atual
  4. varrer até uma aba, somando os valores ASCII modulo sua contagem de arquivos (possivelmente subtraindo 31 de cada caractere primeiro), para obter o código
  5. digitalize até uma nova linha ou EOF
  6. o conteúdo é mmap 'd. esta é uma matriz. escreva desde a posição inicial até a nova linha até um arquivo de saída. Use write(2) , não fputs ou algo assim, para manter o buffer da biblioteca C. fora do caminho.
  7. retorna para 3 até o término do arquivo

O kernel amigável cuidará das coisas de paginação na memória quando você chegar ao fim, assim você não precisará armazenar em buffer.

Observe que o IO mapeado em memória não é necessariamente mais rápido do que read / write chamadas de I / O em massa, o que é efetivamente, mas tornará o código substancialmente mais simples do que tentar escrever a própria lógica do buffer. Uma solução Python baseada nesse design geral também pode ser rápida o suficiente.

    
por 21.05.2013 / 03:06
0

Você poderia escrever um script python para fazer isso ... já que você disse que ele precisa ser rápido, talvez um CRC seja uma opção razoável de função hash.

Tente algo assim:

import fileinput
import binascii

for line in fileinput.input():
    modulo = binascii.crc32(line.split()[0]) % splits

A variável splits deve ser definida para o número de arquivos nos quais você deseja dividir a entrada. Você pode usar a variável modulo para construir o nome do arquivo onde cada linha deve ser colocada.

    
por 21.05.2013 / 01:54
0

Esse problema (suspeito de olhar para casa;) parece um trabalho para awk

awk '{ print > "FilePrefix."$1%YourModValueHere }'

por exemplo

awk '{ print > "bar."$1%3 }'

Atualizar para corrigir mal-entendidos:

1) define outputfilePrefix and modoloValue
2) load inputfile linewise as positional parameters
3) iterate over all entries in the first column
   a) calculate CRC (cksum), and modolo CRC
   b) output first positional parameter ($1) to file (prefix.modoloOfCRC )
   c) shift positional parameters one to the left (discarding the current line in position 1)

código: basta digitar como linha única no bash

preFix="bar"; modolo=3;IFS=$'\n';set $(cat foo); for i in $(cut -f1 foo);do target=$(( $(echo $i | cksum | cut -d ' ' -f1;) % $modolo ));echo $1 >> $preFix.$target; shift; echo $target; done

melhor legível para entender

1) preFix="bar"; modolo=3;
2) IFS=$'\n';set $(cat foo); 
3) for i in $(cut -f1 foo);do 
       target=$(( $(echo $i | cksum | cut -d ' ' -f1;) % $modolo ));
       echo $1 >> $preFix.$target; shift; echo $target; 
   done

se você colocá-lo em um shellscript, você pode até mesmo canalizar o arquivo (com pouca modificação ..) via stdin

    
por 21.05.2013 / 00:34

Tags