web-dev-qa-db-fra.com

Imprimer des lignes dans un fichier correspondant aux modèles dans un autre fichier

J'ai un fichier avec plus de 40 000 lignes (fichier1) et je veux extraire les lignes correspondant aux modèles du fichier2 (environ 6 000 lignes). J'utilise grep comme ça, mais c'est très lent: grep -f file2 file1 > out

Existe-t-il un moyen plus rapide de le faire en utilisant awk ou sed?

Voici quelques extraits de mes fichiers:

File1:
scitn003869.2| scign003869 CGCATGTGTGCATGTATTATCGTATCCCTTG
scitn007747.1| scign007747  CACGCAGACGCAGTGGAGCATTCCAGGTCACAA
scitn003155.1| scign003155  TAAAAATCGTTAGCACTCGCTTGGTACACTAAC
scitn018252.1| scign018252  CGTGTGTGTGCATATGTGTGCATGCGTG
scitn004671.2| scign004671  TCCTCAGGTTTTGAAAGGCAGGGTAAGTGCT
File2:
scign000003
scign000004
scign000005
scign004671
scign000013

"

16
Jon

Essayez grep -Fwf file2 file1 > out

Le -F option spécifie la correspondance des chaînes simples, donc devrait être plus rapide sans avoir à engager le moteur d'expression régulière.

27
glenn jackman

Voici comment le faire dans awk:

awk 'NR==FNR{pats[$0]; next} $2 in pats' File2 File1

En utilisant un fichier1 de 60 000 lignes (votre fichier1 a été répété 8 000 fois) et un fichier 6 0002 (le vôtre s'est répété 1 200 fois):

$ time grep -Fwf File2 File1 > ou2

real    0m0.094s
user    0m0.031s
sys     0m0.062s

$ time awk 'NR==FNR{pats[$0]; next} $2 in pats' File2 File1 > ou1

real    0m0.094s
user    0m0.015s
sys     0m0.077s

$ diff ou1 ou2

c'est-à-dire qu'il est à peu près aussi rapide que le grep. Une chose à noter cependant est que la solution awk vous permet de choisir un champ spécifique pour correspondre, donc si quelque chose de File2 apparaît ailleurs dans File1, vous n'obtiendrez pas une fausse correspondance. Il vous permet également de faire correspondre un champ entier à la fois, donc si vos chaînes cibles étaient de différentes longueurs et que vous ne vouliez pas que "scign000003" corresponde à "scign0000031" par exemple (bien que -w pour grep offre une protection similaire pour cela).

Pour être complet, voici le timing pour l'autre solution awk publiée elsethread:

$ time awk 'BEGIN{i=0}FNR==NR{a[i++]=$1;next}{for(j=0;j<i;j++)if(index($0,a[j]))print $0}' File2 File1 > ou3

real    3m34.110s
user    3m30.850s
sys     0m1.263s

et voici le timing que je reçois pour le script Perl que Mark a publié:

$ time ./go.pl > out2

real    0m0.203s
user    0m0.124s
sys     0m0.062s
14
Ed Morton

Vous pouvez essayer avec cet awk:

awk 'BEGIN{i=0}
FNR==NR { a[i++]=$1; next }
{ for(j=0;j<i;j++)
    if(index($0,a[j]))
        {print $0;break}
}' file2 file1

Le FNR==NR part spécifie que les éléments qui le suivent entre accolades ne doivent être appliqués que lors du traitement du premier fichier d'entrée (file2). Et il dit de sauvegarder tous les mots que vous recherchez dans un tableau a[]. Le bit du deuxième ensemble d'accolades s'applique au traitement du deuxième fichier ... à mesure que chaque ligne est lue, elle est comparée à tous les éléments de a[] et le cas échéant, la ligne est imprimée. C'est tout le monde!

6
Mark Setchell

Juste pour le plaisir d'apprendre: je résolvais le même problème et j'ai trouvé différentes solutions (dont read $line boucles etc ..). Quand je suis arrivé au grep one-liner trouvé ci-dessus, j'ai quand même fini par obtenir la mauvaise sortie. Puis j'ai réalisé que mon fichier PATTERN avait 2 lignes de fin ... Alors grep a récupéré toutes mes lignes de ma base de données. Moralité: vérifiez vos espaces/lignes de fin. De plus, la commande a été exécutée sur un ensemble de données beaucoup plus grand avec plusieurs centaines de modèles et time ne pouvait même pas compter.

4
adrien

Juste pour le plaisir, voici une version Perl:

#!/usr/bin/Perl
use strict;
use warnings;
my %patterns;
my $srch;

# Open file and get patterns to search for
open(my $fh2,"<","file2")|| die "ERROR: Could not open file2";
while (<$fh2>)
{
   chop;
   $patterns{$_}=1;
}

# Now read data file
open(my $fh1,"<","file1")|| die "ERROR: Could not open file1";
while (<$fh1>)
{
   (undef,$srch,undef)=split;
   print $_ if defined $patterns{$srch};
}

Voici quelques timings, en utilisant un fichier1 de 60000 lignes et un fichier2 de 6000 lignes par méthode de création de fichier d'Ed:

time awk 'NR==FNR{pats[$0]; next} $2 in pats' file2 file1 > out
real    0m0.202s
user    0m0.197s
sys     0m0.005s

time ./go.pl > out2
real    0m0.083s
user    0m0.079s
sys     0m0.004s
4
Mark Setchell