Comment couper un fichier en morceaux plus efficacement?

4

Supposons que je dispose d’un fichier texte de 10 Mo foo.txtet de 100 000 lignes. Maintenant, je veux traiter foo.txtfenêtre par fenêtre, avec une taille de fenêtre de 10.

Mon script actuel est comme ça:

for ((i=0;i<$lines;i=i+$step))
do    
    head -$((i+step)) $1 | tail -$step > tmp1
    head -$((i+step)) $2 | tail -$step > tmp2
    setstr=$setstr' '`./accuracy.sh tmp1 tmp2`
done
echo $setstr | awk '{for (i=1;i<=NF;i++) sum+=$i; }END{print sum/NF}'

Mais ça coule lentement. Existe-t-il un moyen simple et plus efficace de procéder?

JackWM
la source

Réponses:

5

Vous pouvez le faire avec split:

Voici un exemple d'utilisation:

split -l 10 input_file output_file_prefix_

L' -loption représente--lines=

Et cela divisera input_fileen morceaux de 10 lignes chacun, dans ces fichiers:

output_file_prefix_aa
output_file_prefix_ab
output_file_prefix_ac
...

etc.

Pour d'autres façons, vous pouvez utiliser split, voir man splitou ici

Sampson-chen
la source
merci, sampson. Un problème avec votre solution est qu’elle produira une grande quantité de fichiers temporels. Dans mon exemple, il s'agirait de 10 000 fichiers. La dernière fois, j’ai utilisé Java pour scinder le fichier et cela m’a pris beaucoup de temps pour les supprimer.
@JackWM Comment avez-vous supprimé les fichiers temporaires? Était-ce quelque chose comme rm output_file_prefix_*?
Samson-chen
rmsignalerait des erreurs dues à la grande quantité de fichiers. Et même si nous contournons cela, la suppression prend encore beaucoup de temps.
1

Il serait utile d’avoir un peu plus de contexte quant à votre objectif ultime plutôt qu’un extrait de code. En particulier, avez-vous un contrôle sur la précision.sh?

Quoi qu’il en soit, si vous voulez continuer à utiliser bash, vous pouvez le faire.

for ((i=0;i<$lines;i+=$step))
do
  let end=i+10
  sed -n $i,${end}p $1 >tmp1
  sed -n $i,${end}p $2 >tmp2
  ...
done
Peter Sundstrom
la source
0

Vous ne savez pas pourquoi cela a migré de StackOverflow. Bien que ce splitsoit une réponse de style superutilisateur , la question portait sur la programmation. Par exemple, voici une réponse qui implémente ce que vous recherchez awk.

L'un des aspects les plus pratiques de awkla façon dont il traite les tuyaux.

#!/usr/bin/awk -f

BEGIN {
  cmd="/path/to/handler"
}

{
  print | cmd
}

NR % 10 == 0 {
  close(cmd)
}

Votre cmdsera ré-ouvert s'il est fermé ... et se ferme toutes les 10 lignes, pour être rouvert la prochaine ligne de sortie.

L'effet sera d'exécuter handlertoutes les 10 lignes d'entrée. À la fin du fichier, handlersera exécuté avec toutes les lignes restantes, comme cela cmdse ferme automatiquement à la sortie de awk.

Strictement parlant, vous n’avez pas besoin d’utiliser une variable comme celle cmdde stocker la commande ... mais cela simplifie le réglage de la commande, car vous auriez sinon besoin de regarder TRÈS attentivement les fautes de frappe dans votre close().

Ghoti
la source
0

Cette solution n'utilise aucun fichier temporaire. Ce qui est fait est de stocker chaque ligne dans un tableau de mémoire tampon pouvant contenir dix lignes. Chaque fois que le numéro de ligne est divisible par dix, toutes les lignes du tampon sont imprimées.

Le piège évident est lorsque le fichier d'entrée (# lignes) n'est pas divisible par dix. La solution consiste à effectuer des vérifications dans une clause END {}. Quelque chose comme:

$ echo {1..33} | tr \ \\ n | \
    awk '{lignes = NR} FIN {if (lignes% 10! = 0) {affiche "lignes restantes"}}'
lignes restantes

# STEP1 utilise modulo pour faire quelque chose tous les dix ans
$ echo {1..200} | tr \ \\ n | \
    awk '{a [NR% 10] = 0 $; if (NR% 10 == 0) {print "ten"}} '| chat -n
     1 dix
     2 dix
     3 dix
     4 dix
     5 dix
     6 dix
     7 dix
     8 dix
     9 dix
    10 dix
    11 dix
    12 dix
    13 dix
    14 dix
    15 dix
    16 dix
    17 dix
    18 dix
    19 dix
    20 dix

# STEP 2 faire quelque chose avec chaque ligne
$ echo {1..10} | tr \ \\ n | awk '{b + = $ 0} END {print b}'
55

# mettre ensemble
$ chat tous les10.awk
{
        a [NR% 10] = 0;
        si (NR% 10 == 0) {
                pour (i dans a) {
                        printf "% s +", un [i]
                        b + = a [i];
                }
                affiche "0 =" b;
                b = 0
        }
}
$ echo {1..200} | tr \ \\ n | awk -f every10.awk | colonne -s = -t
4 + 5 + 6 + 7 + 8 + 9 + 10 + 1 + 2 + 3 + 0 55
14 + 15 + 16 + 17 + 18 + 19 + 20 + 11 + 12 + 13 + 0 155
24 + 25 + 26 + 27 + 28 + 29 + 30 + 21 + 22 + 23 + 0 255
34 + 35 + 36 + 37 + 38 + 39 + 40 + 31 + 32 + 33 + 0 355
44 + 45 + 46 + 47 + 48 + 49 + 50 + 41 + 42 + 43 + 0 455
54 + 55 + 56 + 57 + 58 + 59 + 60 + 51 + 52 + 53 + 0 555
64 + 65 + 66 + 67 + 68 + 69 + 70 + 61 + 62 + 63 + 0 655
74 + 75 + 76 + 77 + 78 + 79 + 80 + 71 + 72 + 73 + 0 755
84 + 85 + 86 + 87 + 88 + 89 + 90 + 81 + 82 + 83 + 0 855
94 + 95 + 96 + 97 + 98 + 99 + 99 + 100 + 91 + 92 + 93 + 0 955
104 + 105 + 106 + 107 + 108 + 109 + 109 + 110 + 101 + 102 + 103 + 0 1055
114 + 115 + 116 + 117 + 118 + 119 + 120 + 111 + 112 + 113 + 0 1155
124 + 125 + 126 + 127 + 128 + 129 + 129 + 130 + 121 + 122 + 123 + 0 1255
134 + 135 + 136 + 137 + 138 + 139 + 140 + 131 + 132 + 133 + 0 1355
144 + 145 + 146 + 147 + 148 + 149 + 150 + 141 + 142 + 143 + 0 1455
154 + 155 + 156 + 157 + 158 + 159 + 160 + 151 + 152 + 153 + 0 1555
164 + 165 + 166 + 167 + 168 + 169 + 170 + 161 + 162 + 163 + 0 1655
174 + 175 + 176 + 177 + 178 + 179 + 179 + 180 + 171 + 172 + 173 + 0 1755
184 + 185 + 186 + 187 + 188 + 189 + 189 + 190 + 181 + 182 + 183 + 0 1855
194 + 195 + 196 + 197 + 198 + 199 + 200 + 191 + 192 + 193 + 0 1955

L'idée est d'utiliser des blocs d'impression awk de dix lignes et de les traiter, ou de les traiter directement avec awk si l'opération est une simple opération arithmétique ou chaîne.

Рослав Рахматуллин
la source