2010-08-27 5 views
3

J'ai deux fichiers texte (A.txt et B.txt) qui ressemblent à ceci (pourrait avoir ~ 10000 lignes chacun)Comment trier des lignes dans un fichier texte en Perl?

processa,id1=123,id2=5321 
processa,id1=432,id2=3721 
processa,id1=3,id2=521 
processb,id1=9822,id2=521 
processa,id1=213,id2=1 
processc,id1=822,id2=521 

Je dois vérifier si chaque ligne dans le fichier A.txt est présent dans B.txt aussi bien (B.txt pourrait avoir plus aussi, c'est correct).

La chose est que les lignes peuvent être dans un ordre quelconque dans les deux fichiers, donc je pense que je vais les trier dans un ordre particulier dans les deux fichiers dans O(nlogn) puis correspondre à chaque ligne A.txt aux lignes suivantes dans B.txt au O(n). Je pourrais mettre en place un hachage, mais les fichiers sont gros et cette comparaison ne se produit qu'une seule fois après laquelle ces fichiers sont régénérés, donc je ne pense pas que ce soit une bonne idée.

Quelle est la meilleure façon de trier les fichiers en Perl? Toute commande ferait, il faut juste une commande.

Par exemple, Comme je l'ai mentionné dans le dictionnaire commande, ce serait

processa,id1=123,id2=5321 
processa,id1=213,id2=1 
processa,id1=3,id2=521 
processa,id1=432,id2=3721 
processb,id1=9822,id2=521 
processc,id1=822,id2=521 

avant, toute commande serait tout aussi bien, aussi longtemps que Perl est rapide à le faire.

Je veux le faire à partir du code Perl, après l'ouverture du fichier comme si

open (FH, "<A.txt"); 

commentaires, idées, etc. seraient utiles.

+1

Si vous faites 10 000 lignes, et que vous envisagez de l'utiliser plus d'une fois - à tout le moins j'utiliserais SQL Lite. –

+5

Un hachage et un tri nécessitent tous les deux de charger le fichier 'A.txt' entier en mémoire. Pourquoi pensez-vous que le tri est préférable à l'utilisation d'un hachage? – mob

+0

@mobrule: Je pensais que Perl pourrait avoir un moyen intelligent de le trier, étant donné que c'est un langage qui est construit pour traiter le texte. Un moyen qui vaut mieux que de mettre en place un hachage moi-même. D'après les réponses ci-dessous, il semble que le hachage soit après tout une bonne idée! – Lazer

Répondre

0

Eh bien, j'analyse régulièrement de très gros fichiers journaux Apache quotidiens (600 Mo) avec Perl, et pour stocker les informations, j'utilise un hachage. Je passe également en revue environ 30 de ces fichiers, dans une instance de script, en utilisant le même hachage. Ce n'est pas un gros problème en supposant que vous avez assez de RAM.

5

Pour trier le fichier dans votre script, vous devrez tout charger dans la mémoire. Si vous faites cela, je ne suis pas sûr de savoir quel est l'avantage de le trier par rapport à un simple hachage?

Quelque chose comme ça fonctionnerait:

my %seen; 
open(A, "<A.txt") or die "Can't read A: $!"; 
while (<A>) { 
    $seen{$_}=1; 
} 
close A; 

open(B, "<B.txt") or die "Can't read B: $!"; 
while(<B>) { 
    delete $seen{$_}; 
} 
close B; 

print "Lines found in A, missing in B:\n"; 
join "\n", keys %seen; 
+0

Il n'est pas nécessaire de charger tout le fichier en mémoire à la fois pour le trier. C'est pourquoi le tri par fusion a été inventé. Mais si vous avez assez de mémoire, c'est une bonne approche. – cjm

0

Puis-je vous demander pourquoi vous devez le faire en Perl natif? Si le coût de l'appel d'un appel système ou 3 n'est pas un problème (par exemple, vous le faites souvent et non pas dans une boucle serrée), pourquoi ne pas simplement faire:

my $cmd = "sort $file1 > $file1.sorted"; 
$cmd .= "; sort $file2 > $file2.sorted"; 
$cmd .= "; comm -23 $file1.sorted $file2.sorted |wc -l"; 
my $count = `$cmd`; 
$count =~ s/\s+//g; 
if ($count != 0) { 
    print "Stuff in A exists that aren't in B\n"; 
} 

S'il vous plaît noter que le paramètre comm peut être différent, selon sur ce que tu veux exactement.

+0

Cette comparaison n'est qu'une petite partie de ce que fait mon script Perl. Ces fichiers sont générés, comparés et traités. Quant à savoir pourquoi je fais tout ça en Perl, j'ai besoin que les choses soient portables, et que je travaille sur une variété d'environnements, pas seulement sur Unix. – Lazer

+0

Eh bien, les deux (tri et comm et probablement wc) devraient aussi être disponibles sur Windows, mais oui, c'est évidemment moins portable, vous avez raison. Il est cependant beaucoup plus optimisé pour les gros fichiers. Si vous avez besoin d'une portabilité Perl pure, la solution de hachage est votre ticket. – DVK

0

test si A.txt est un sous-ensemble de B.txt

open FILE.B, "B.txt"; 
open FILE.A, "A.txt"; 

my %bFile; 

while(<FILE.B>) { 
    ($process, $id1, $id2) = split /,/; 
    $bFile{$process}{$id1}{$id2}++; 
} 

$missingRows = 0; 

while(<FILE.A>) { 
    $missingRows++ unless $bFile{$process}{$id1}{$id2}; 
    # If we've seen a given entry already don't add it 
    next if $missingRows; # One miss means they aren't all verified 
} 

$is_Atxt_Subset_Btxt = $missingRows?FALSE:TRUE; 

Cela vous donnera un test pour toutes les lignes A être en B à la lecture que dans tous B puis tester chaque membre du réseau lors de la lecture A.

+0

Non, je n'ajoute ou ne supprime aucune information dans les fichiers. Je réarrange juste ce qui est déjà là. L'idée est de vérifier si 'A.txt' est un sous-ensemble de' B.txt' ou non. – Lazer

+0

@Lazer: Mise à jour pour faire exactement cela. – HerbN

+0

thaanks @HerbN! – Lazer

0

Comme d'habitude, CPAN a une réponse pour cela. Soit Sort::External ou File::Sort semble que cela fonctionnerait.Je n'ai jamais eu l'occasion d'essayer non plus, alors je ne sais pas ce qui serait mieux pour vous.

Une autre possibilité consiste à utiliser AnyDBM_File pour créer un hachage sur disque pouvant dépasser la mémoire disponible. Sans l'essayer, je ne pourrais pas dire si l'utilisation d'un fichier DBM serait plus rapide ou plus lente que le tri, mais le code serait probablement plus simple.

1

Voici une autre façon de le faire. L'idée est de créer une structure de données flexible qui vous permet de répondre facilement à de nombreux types de questions avec grep.

use strict; 
use warnings; 

my ($fileA, $fileB) = @ARGV; 

# Load all lines: $h{LINE}{FILE_NAME} = TALLY 
my %h; 
$h{$_}{$ARGV} ++ while <>; 

# Do whatever you need. 
my @all_lines = keys %h; 
my @in_both = grep {  keys %{$h{$_}} == 2  } keys %h; 
my @in_A  = grep {  exists $h{$_}{$fileA}  } keys %h; 
my @only_in_A = grep { not exists $h{$_}{$fileB}  } @in_A; 
my @in_A_mult = grep {   $h{$_}{$fileA} > 1 } @in_A; 
+0

Que se passe-t-il si une ligne donnée apparaît deux fois dans un fichier mais zéro dans l'autre? – HerbN

+0

@HerbN Cela fonctionne très bien. Le N de touches dans '% {$ h {LINE}}' vous indique le nombre de fichiers d'une LIGNE particulière. Et les paramètres sont spécifiques au fichier: chaque pointage vous indique le N fois où une LIGNE particulière s'est produite dans un FICHIER particulier . – FMc

Questions connexes