J'ai un fichier contenant environ 10 millions de lignes.
Je souhaite supprimer toutes les lignes du fichier contenant moins de six caractères.
Comment puis-je faire cela?
J'ai un fichier contenant environ 10 millions de lignes.
Je souhaite supprimer toutes les lignes du fichier contenant moins de six caractères.
Comment puis-je faire cela?
Réponses:
Il y a plusieurs façons de procéder.
En utilisant grep
:
grep -E '^.{6,}$' file.txt >out.txt
Contiendra désormais out.txt
des lignes de six caractères ou plus.
Inverse:
grep -vE '^.{,5}$' file.txt >out.txt
Utilisation sed
, suppression de lignes de longueur 5 ou moins:
sed -r '/^.{,5}$/d' file.txt
Inverser, imprimer des lignes de longueur six ou plus:
sed -nr '/^.{6,}$/p' file.txt
Vous pouvez enregistrer la sortie dans un fichier différent en utilisant l' >
opérateur comme grep
ou modifier le fichier sur place en utilisant l' -i
option de sed
:
sed -ri.bak '/^.{6,}$/' file.txt
Le fichier d'origine sera sauvegardé en tant que file.txt.bak
et le fichier modifié sera file.txt
.
Si vous ne souhaitez pas conserver de sauvegarde:
sed -ri '/^.{6,}$/' file.txt
Utiliser shell, Slower, Don't do this , c'est juste pour montrer une autre méthode:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
L' utilisation python
, encore plus lent que grep
, sed
:
#!/usr/bin/env python2
with open('file.txt') as f:
for line in f:
if len(line.rstrip('\n')) >= 6:
print line.rstrip('\n')
Mieux utiliser la compréhension des listes pour être plus Pythonic:
#!/usr/bin/env python2
with open('file.txt') as f:
strip = str.rstrip
print '\n'.join([line for line in f if len(strip(line, '\n')) >= 6]).rstrip('\n')
C'est très simple:
grep ...... inputfile > resultfile #There are 6 dots
Ceci est extrêmement efficace, car grep
il n'essaiera pas d'analyser plus que ce dont il a besoin, ni d'interpréter les caractères de quelque manière que ce soit: il envoie simplement une ligne (entière) à stdout (que le shell redirige ensuite vers le fichier de résultats) dès qu'il a vu 6 les caractères sur cette ligne ( .
dans un contexte d'expression régulière correspond à n'importe quel caractère).
Ainsi, grep ne sortira que les lignes ayant 6 (ou plus) caractères, et les autres ne sont pas sorties par grep, donc elles ne parviennent pas au fichier de résultats.
Méthode la plus rapide: compilez et exécutez ce programme C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BUFFER_SIZE 1000000
int main(int argc, char *argv[]) {
int length;
if(argc == 3)
length = atoi(argv[2]);
else
return 1;
FILE *file = fopen(argv[1], "r");
if(file != NULL) {
char line[MAX_BUFFER_SIZE];
while(fgets(line, sizeof line, file) != NULL) {
char *pos;
if((pos = strchr(line, '\n')) != NULL)
*pos = '\0';
if(strlen(line) >= length)
printf("%s\n", line);
}
fclose(file);
}
else {
perror(argv[1]);
return 1;
}
return 0;
}
Compilez avec gcc program.c -o program
, exécutez avec ./program file line_length
(où file
= chemin d'accès au fichier et line_length
= longueur de ligne minimale, dans votre cas 6
; la longueur de ligne maximale est limitée à des 1000000
caractères par ligne; vous pouvez modifier cela en modifiant la valeur de MAX_BUFFER_SIZE
).
(Astuce pour remplacer \n
par \0
trouvé ici .)
Comparaison avec toutes les autres solutions proposées à cette question à l'exception de la solution shell (test exécuté sur un fichier ~ 91MB avec 10M lignes avec une longueur moyenne de 8 caractères):
time ./foo file 6
real 0m1.592s
user 0m0.712s
sys 0m0.160s
time grep ...... file
real 0m1.945s
user 0m0.912s
sys 0m0.176s
time grep -E '^.{6,}$'
real 0m2.178s
user 0m1.124s
sys 0m0.152s
time awk 'length>=6' file
real 0m2.261s
user 0m1.228s
sys 0m0.160s
time perl -lne 'length>=6&&print' file
real 0m4.252s
user 0m3.220s
sys 0m0.164s
sed -r '/^.{,5}$/d' file >out
real 0m7.947s
user 0m7.064s
sys 0m0.120s
./script.py >out
real 0m8.154s
user 0m7.184s
sys 0m0.164s
awk 'length>=6' file
length>=6
: si length>=6
renvoie VRAI, imprime l'enregistrement en cours.perl -lne 'length>=6&&print' file
lenght>=6
renvoie VRAI, imprime l'enregistrement en cours.% cat file
a
bb
ccc
dddd
eeeee
ffffff
ggggggg
% ./foo file 6
ffffff
ggggggg
% awk 'length>=6' file
ffffff
ggggggg
% perl -lne 'length>=6&&print' file
ffffff
ggggggg
awk
solution ..
sed
solution (ça arrive, je sais). XD
pos
variable? Je comprends qu'il retourne un pointeur sur le caractère line
avec un caractère de nouvelle ligne, mais vous ne semblez jamais l'utiliser. Et si vous ne le trouvez pas, vous le définissez simplement égal à \0
.
\0
( strchr()
renvoie un pointeur NULL si le caractère n'est pas trouvé). Le point consiste à remplacer chaque nouvelle ligne à la fin de chaque ligne par \0
afin que la nouvelle ligne ne soit jamais comptée par strlen()
: c'est pour que la longueur puisse toujours être comparée à 6, indépendamment d'une nouvelle ligne potentiellement manquante sur la dernière ligne. Traiter différemment seule la dernière ligne serait beaucoup plus efficace, je sais. Je mettrai probablement cela à jour plus tard.
grep
solution sur le même fichier et c'est en fait plus rapide (probablement parce que ce strlen()
n'est pas la meilleure idée ici) . Je vais essayer d'utiliser une getchar()
boucle afin de ne vérifier que le premier caractère N à la place, je suppose que cela devrait l'améliorer visiblement. Et oui, toute ligne au-dessus de la longueur du tampon est simplement coupée à la longueur du tampon.
Vous pouvez utiliser Vim en mode Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v
allume la magie
.{6}
rechercher des lignes avec 6 caractères ou plus
v
inverser la sélection
d
supprimer
x
sauver et fermer
Solution Ruby:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
Idée simple: rediriger le fichier vers stdin de ruby et imprimer la ligne depuis stdin uniquement si sa longueur est supérieure ou égale à 6