IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Comparaison de deux fichiers volumineux


Sujet :

Langage Perl

  1. #21
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Citation Envoyé par Lolo78
    Dans le cas général, pour moi, la solution tri + appareillage est incontournable.
    Je suis aussi de cet avis.
    Et existe t-il en dehors de COBOL , en Perl par exemple puisque ce forum y est dédié, un module de dessous les fagots...
    la possibilité de regrouper les lectures de fichier, de grouper des enregistrements logiques en enregistrements physiques, de façon à limiter les interruptions CPU en faisant des accès disques qui ramènent n enregistrements logiques d'un coup ?
    Qui a vu le comportement d'un lecteur de bande ancien accédé avec ou sans facteur de groupage comprendra ma question
    Ne me dites pas qu'avec les disques logiques, c'est périmé...
    En BDD je m'arrangeait pour mettre Tables et Index sur disques logiques différents, et même de séparer les grosses tables faisant souvent partie d'une jointure SQL... la différence est sensible !

    Désolé, de faire comme si je m'adressais à des novices, ça fait au moins 30 ans que je n'avais pas entendu le mot appareillage
    Ma question est sérieuse, mais seulement par curiosité, ça fait des lustres que je ne manipule plus de Go !

  2. #22
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    En situation normale, Perl bufferise les entrées/sorties, c'est à dire qu'en lecture de fichier, par exemple, quand on demande une ligne, Perl lit en mémoire toute une page (de par exemple 8 ko), fournit la ligne et garde en mémoire le reste pour la prochaine demande de ligne. Avec un peu de chance, les lignes suivantes sont même mises en cache par le processeur.

    La méthode que j'ai présentée (tri + appareillage) est au moins 10 fois plus rapide que de passer par une base de donnée. En effet, une BD doit trier les données pour construire ses index, cela prend à peu près le même temps qu'un sort Unix. Ensuite, le programme d'appareillage lit chacun des deux fichiers séquentiellement une seule fois, ce qui va bien plus vite d'un accès indexé à tous les enregistrements de l'un des deux fichiers. Avec 2 fichiers de 2 giga chacun, la durée d'exécution est de l'ordre de 35 minutes, 15 minutes pour chaque tri et 5 minutes pour l'appareillage (dans la pratique, cependant, je fais d'autres choses en même temps (dédoublonnage croisé, exclusion d'anomalies, etc.)

    J'ai écrit un module Perl qui fait cet appareillage, compare 2 fichiers A et B, et met dans un fichier les orphelins A, dans un autre fichier les orphelins B et dans un troisième fichier les données communes. Mais il nécessite l'utilisation de fonctions de rappel définissant comment on compare deux enregistrements.

  3. #23
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    @Lolo78
    MERCI pour ta réponse détaillé !
    En situation normale, Perl bufferise les entrées/sorties, c'est à dire qu'en lecture de fichier, par exemple, quand on demande une ligne, Perl lit en mémoire toute une page (de par exemple 8 ko), fournit la ligne et garde en mémoire le reste pour la prochaine demande de ligne. Avec un peu de chance, les lignes suivantes sont même mises en cache par le processeur.
    Oui çà je sais, je parlais d'un gros facteur de blocage spécifié dans le programme...
    Faire confiance au langage, à l'OS... c'est bien, mais c'est mieux de se rendre compte par soi-même, quand c'est possible bien sûr !
    en COBOL quand je passais (syntaxe de mémoire) de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BLOCK CONTAINS 100 RECORDS
    à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BLOCK CONTAINS 5000 RECORDS
    je divisais le temps du traitement total par 10 ou 20...
    Ce n'était pas 8k en mémoire, mais qq Mo... 4mn au lieu d'une heure, ce n'est pas rien !

    La méthode que j'ai présentée (tri + appareillage) est au moins 10 fois plus rapide que de passer par une base de donnée. En effet, une BD doit trier les données pour construire ses index, cela prend à peu près le même temps qu'un sort Unix.
    Là tu prêches un convaincu, dès qu'un traitement lit plus de 50% d'un fichier il est préférable de le faire en séquentiel. Les BDD ne sont pas faites pour faire du traitement par lot.
    Sur une BDD on en arrive à inhiber les index (HINTS en Oracle) pour ne pas faire de produits cartésiens à la fois sur les données et les index.
    Là on peut diviser le temps allègrement par 10 000 !
    Au début (en v5) les HINTS n'existaient pas on se débrouillait en ajoutant 0 à une variable numérique et une chaine vide... çà donnait quelque chose comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Where Tab1_ValNun = Tab2_ValNum + 0
    And   Tab4_ValChain = Tab8_ValChain + "";
    Là l'analyseur SQL découvrait une opération sur un index, il ne l'utilisait plus.
    Dans un cas l'utilisateur attendait sa réponse 10mn, dans l'autre c'était instantané

    J'ai le sentiment, que c'est quelque chose qui a été perdu de vue...
    Bien sûr les accès disque sont toujours de plus en plus performants, n'empêche que les entrées/sorties ont toujours été, et restent, le goulot d'étranglement...
    En tenir compte même sur des supports rapides ne peut qu'améliorer les performances, au moins en attendant d'avoir des volumes logiques de SSD en Raid

  4. #24
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Hmm, Cobol, j'en ai fait très peu, uniquement pendant mes études, jamais dans la vraie vie, et j'ai tout oublié (grand bien me fasse).

    Sur ton autre point. J'avais un programme PL-SQL qui lisait séquentiellement une très grosse table Oracle et, pour chaque enregistrement, allait chercher en base quantités d'infos supplémentaires afin de produire un gros fichier résultat. Durée d'exécution pour une période comptable mensuelle: une dizaine d'heures. Trop lourd pour l'extraction envisagée de 48 périodes comptables (4 ans). Je l'ai d'abord ré-écrit en Perl (avec DBI, DBD::Oracle, etc.), en utilisant presque la même logique de programmation. Résultat, environ 4 heures. C'est déjà une très bonne amélioration, et ça peut suffire dans certains cas, mais pas ici. Deuxième réécriture en Perl, avec chargement de toute une série de données de paramétrage dans des hash pour un accès plus rapide. Résultat: 45 minutes. Nouvelle modification avec chargement des données clients et quelques autres en hash. Résultat: environ 5 mn. J'avais quelques pistes d"optimisation supplémentaire, mais là, plus vraiment besoin, 5 mn * 48 périodes = 240 mn (4 heures), plus aucun problème de performance: 4 heures sur une bécane de test ou de pré-prod, ça ne gêne personne.

  5. #25
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Citation Envoyé par Lolo78
    Hmm, Cobol, j'en ai fait très peu, uniquement pendant mes études, jamais dans la vraie vie, et j'ai tout oublié (grand bien me fasse).
    J'en ai peu fait aussi, et je ne le regrette pas
    J'arrête là pour ne pas pourrir le sujet de shinjidragibus.
    Content d'avoir rencontré quelqu'un qui à le souci des performances
    @+

  6. #26
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Bonjour,
    Juste une question aux spécialistes, si on mets les fichiers entiers en tableau, que se passe t-il s'il n'y a pas assez de mémoire, ça plante ou ça swape ?

    Du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    #!/usr/bin/perl
    use warnings;
    use strict;
    use utf8;
     
    my $Date_Heure = date();
    print "\nTIME=$Date_Heure\n\n";
    my ($Fic1, $Fic2) = @ARGV;
     
    #Ouverture fichiers 
    open(IN1, $Fic1 ) || die "ERROR - $! : $Fic1\n";
    open(IN2, $Fic2 ) || die "ERROR - $! : $Fic2\n";
     
    # Chargement en tableau
    my @File1 =  <IN1>;
    my @File2 =  <IN2>;
    close IN1;
    close IN2;
     
    # Elimination des doublons, lignes vides...
    @File1 = doublons_grep( \@File1 );
    @File2 = doublons_grep( \@File2 );
     
    # Mise en Hash de File1
    my %Temp;
    @Temp{@File1} = 0..$#File1;
     
    for my $Ligne (@File2) {
    	if( exists $Temp{$Ligne} ) {
    		# print "$Ligne est présente dans Fic1 à la position $Temp{$Ligne}.\n\n";
    	} else {
    		print "$Ligne n'est pas dans Fic1.\n\n";
    	}
    }
     
    $Date_Heure = date();
    print "\nTIME=$Date_Heure\n";
     
    #######################################
    # Elimination des doublons d'un tableau
    sub doublons_grep {
      my ($ref_tabeau) = @_;
      my %hash_sans_doublon;
      return grep { !$hash_sans_doublon{$_}++ } @{$ref_tabeau};
    }
     
    ###############
    # Date et Heure
    sub date {
      my $time = shift || time;    #$time par defaut vaut le time actuel
      my ( $seconde, $minute, $heure, $jour, $mois, $annee, $jour_semaine, $jour_annee, $heure_hiver_ou_ete )
        = localtime($time);
      $mois  += 1;
      $annee += 1900;
     
      # On rajoute 0 si le chiffre est compris entre 1 et 9
      foreach ( $seconde, $minute, $heure, $jour, $mois, $annee ) { s/^(\d)$/0$1/; }
      return "Le $jour/$mois/$annee à $heure:$minute:$seconde";
    }
    Pas nécessairement plus performant que vos propositions !
    Mais il serait quand même plus logique que ça swape uniquement !
    Je n'ai pas de gros fichiers à tester...
    Merci d'avance !

  7. #27
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Ça doit dépendre de l'OS, mais en général, d'après mon expérience, ça plante avec un message "Out of memory".

  8. #28
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Citation Envoyé par Lolo78
    Ça doit dépendre de l'OS, mais en général, d'après mon expérience, ça plante avec un message "Out of memory".
    MERCI Lolo78, c'est effectivement ce que j'ai vu jusqu'à présent, j'aurais préféré qu'il swape, quitte à dégrader les performances, sinon à quoi bon un swap ?
    Ceci dit, étant un adepte de la liberté de choisir... j'aime bien la solution de Philou67430 !

  9. #29
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    J'aurais tendance à dire qu'il swape, puis qu'il plante, mais cela doit dépendre de l'OS.
    La solution du tri+appareillage unix (diff) qu'à proposé Jedaï semble "viable", mais plus consommatrice en ressources (mémoire et CPU) que celle de la comparaison "par parties" que j'ai proposé et que j'avais déjà utilisé par le passé pour d'autres problématiques plus complexes que de la simple recherche de présence (problèmes de linguistiques de "étoile de mer" sur ce forum).

    Le problème que je vois dans le tri c'est qu'il en fait trop pour le besoin, et donc, même si la solution est très simple, elle ne tourne pas forcément sur toutes les machines (à cause des ressources qu'elles nécessite). Je viens de faire le même essai que précédemment mais avec des fichiers 2x plus gros (2Go) et le diff s'est planté après que les "sort" ait réussi mais au bout d'une heure. Certes, l'appareillage perl n'aurait pas planté, et des options de diff permettent sans doute de limiter la mémoire consommée.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  10. #30
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Bonjour Philou67430,
    MERCI pour ta réponse !
    Ce qui me turlupinait c'était "Out of memory" sur un Unix/linux qui a un swap. Sur Windows je ne sais pas, sur un Unix il devrait sortir avec un "Out of swap"
    C'est il me semble ce que devrait donner la solution de Jedai en cas de débordement.
    cmp/diff devraient toujours être possibles sur Unix, en invitant l'utilisateur à augmenter le swap si nécessaire... l'utilisateur doit pouvoir savoir à quoi s'en tenir !
    Je ne parle pas des performances bien sûr très dégradées...
    Si un jour l'un d'entre-vous tombe en Unix sur un "Out of swap" au lieu d'un "Out of memory" ça m'intéresse, toujours par curiosité !
    Mais ne perdez pas de temps à faire l'essai !

    J'ai testé ton programme, mais sur de petits fichiers, le principe me plait !

    Encore MERCI !

  11. #31
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Comme le SWAP est une mémoire (sur un autre support que de la RAM, ça pourrait être du SSD par exemple), "Out of memory" n'est donc pas déconnant
    Par ailleurs, le SWAP est utilisé pour "délester" la mémoire d'autres processus au profit du processus demandeur. Si un processus requiert plus que la mémoire RAM disponible, il y aura de toute façon "Out of memory", puisque ses propres pages ne pourront pas être "swapped out"... enfin, c'est la compréhension que j'ai du swap POSIX.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  12. #32
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Je suis d'accord avec ton commentaire !
    Il y a très longtemps j'ai eu des "Out of swap" mais je ne me souviens plus dans qu'elles conditions...

  13. #33
    Futur Membre du Club
    Inscrit en
    Février 2007
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 15
    Points : 6
    Points
    6
    Par défaut
    Je vois que ça a pas mal discuté pendant mon absence

    juste un petit retour suite à votre aide. le script tourne très bien! mon collègue a pu travailler avec c'est cool! merci encore pour votre expertise

    J'ai un peu de temps actuellement donc je vais essayer d'aller encore un peu plus loin pour lui simplifier la vie, et je vais chercher une solution pour trouver les différences entre deux lignes.

    Ex: fic1 "toto tata tutu toto" fic2 "toto titi tutu toto" me ressortira un truc du genre "toto tata tutu toto / toto titi tutu toto"

    comme ça mon 1er script ressortira les lignes différentes dans mes fichiers, et le 2nd comparera ces extractions et fera ressortir les différence ligne à ligne (je pense avec un color coding genre mettre en rouge le mot différent)!

    bref j'ai de quoi m'amuser!

    Merci encore! et heureux de voir que mon sujet a pu lancer des débats

  14. #34
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Pour ce genre d'objectif, la solution de lolo est à privilégier. En effet, la solution du hash telle que je l'ai proposée n'est plus utilisable.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  15. #35
    Futur Membre du Club
    Inscrit en
    Février 2007
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 15
    Points : 6
    Points
    6
    Par défaut
    Citation Envoyé par Philou67430 Voir le message
    Pour ce genre d'objectif, la solution de lolo est à privilégier. En effet, la solution du hash telle que je l'ai proposée n'est plus utilisable.
    je vais regarder ça!

    au fait voici le script que j'utilise actuellement (grâce à toi):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    #!/usr/bin/perl
     
    use strict;
    use warnings;
    use feature qw(:5.10);
     
    #On crée le fichier de destination
    open (EXTRACTION1, ">Compar_$ARGV[0]_$ARGV[1].log") || die ("Vous ne pouvez pas créer le fichier \"Comp_$ARGV[0]_$ARGV[1].log\"");  
    my $max_size;
    sub cmp_big($$$) {
      my ($f1, $f2, $f2_info) = @_;
     
     
      open my $F1, "<", $f1 or die "Vous ne pouvez pas ouvrir $f1";
     
    #compte le nombre de ligne du fichier 1
     	my $F1_line_count=0;
    	open F1, $f1 or die $!; 
    	while (<F1>) {
               $F1_line_count ++;
    	}
    	close (F1);
     
     open (EXTRACTION2, ">Compar_Ligne_non_presente_dans_$f2.log") || die ("Vous ne pouvez pas créer le fichier \"Comp_$f1.log\"");
    #comparaison des fichiers  
      say EXTRACTION1 "Chargement du fichier $f1";
      while (!eof $F1) {
        my %F1;
        while (keys %F1 < $max_size && defined(my $l1 = <$F1>)) {
          chomp($l1);
          $F1{$l1}++;
        }
        print "Mise en memoire des lignes $./$F1_line_count => debut de la comparaison.\n";
        open my $F2, "<", $f2 or die "Cant open $f2";
        while (my $l2 = <$F2>) {
          chomp $l2;
          if (exists $F1{$l2}) {
            delete $F1{$l2} if --$F1{$l2} == 0;
          }
        }
        say EXTRACTION1 "[$_] n'est pas presente dans le $f2_info $f2 ($F1{$_})" foreach keys %F1;
        say EXTRACTION2 "[$_]" foreach keys %F1;
        say "Suite du chargement du fichier $f1";
      }
      close (EXTRACTION2);
    }
     
    $max_size = $ARGV[2] // 1000000;
    cmp_big($ARGV[0], $ARGV[1], "Second fichier");
    cmp_big($ARGV[1], $ARGV[0], "Premier fichier"); 
     
    close (EXTRACTION1);
    si jamais quelqu'un en a besoin! en gros il crée 3 fichiers, un global avec les diff des 2 fichiers, et un pour chaque fichier contenant ses différences avec l'autre fichier.

  16. #36
    Membre chevronné Avatar de dmganges
    Homme Profil pro
    Retraité. Ne recherche pas un emploi.
    Inscrit en
    Septembre 2011
    Messages
    1 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Retraité. Ne recherche pas un emploi.
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2011
    Messages : 1 392
    Points : 2 044
    Points
    2 044
    Par défaut
    Bonjour shinjidragibus et MERCI pour le retour,
    Comme j'ai évoqué l'appareillage dans ma première réponse et que je me paye une grosse insomnie, voici un premier jet :

    Appareillage.pl
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
     
    #!/usr/bin/perl
    use warnings;
    use strict;
    use utf8;
     
    ActiverAccents();
     
    print "Usage : \n\nperl appareillage.pl E:/_DEV/_Fichiers/sortedFile1 E:/_DEV/_Fichiers/sortedFile2\n\n";
     
    my $Date_Heure = date();
    print "\nTIME=$Date_Heure\n\n";
    my ( $FicA,   $FicB )   = @ARGV;
    my ( $LigneA, $LigneB ) = "";
    my ( $EOFA, $EOFB, $NbA, $NbB ) = 0;
     
    print "\nFICHIERS \n=$ARGV[0]=\n=$ARGV[1]=\n\n";
     
    #Ouverture fichiers lecture 1er enreg
    open my $fhA, '<:utf8', $FicA or die "ERROR - $! : $FicA\n";
    if ( !($LigneA = <$fhA>)) {
    	print "Arrêt FICA $FicA vide\n- $!\n";
    	exit;
    }else {
    	$NbA++;
    }
    open my $fhB, '<:utf8', $FicB or die "ERROR - $! : $FicB\n";
    if ( !($LigneB = <$fhB>)) {
    	print "Arrêt FICB $FicB vide\n- $!\n";
    	exit;
    } else {
    	$NbB++;
    }
     
    # Appareillage
    while ( ! $EOFA || ! $EOFB ) {
    	if ( $LigneA eq $LigneB ) {
    	#	print "\nExiste dans les deux fichiers :\n";
    	#	print "LIGNEA=$NbA=$LigneA=\n";
    	#	print "LIGNEB=$NbB=$LigneB=\n";
    		if ( !($LigneA = <$fhA>) ) {
    			print "=======================================FICA Fini\n\n";
    			$LigneA = "x{FFFF}";
    			$EOFA = 1;
    		} else {
    			$NbA++;
    		}
    		if ( !($LigneB = <$fhB>) ) {
    			print "=======================================FICB Fini\n\n";
    			$LigneB = "x{FFFF}";
    			$EOFB = 1;
    		} else {
    			$NbB++;
    			next if ( $LigneB eq '\n' );
    		}
    	} else {
    		if ( $LigneA lt $LigneB && ! $EOFA ) {
    			print "\nSuppresion dans FICB :\n";
    			print "LIGNEA=$NbA=$LigneA=\n";
    			if ( !($LigneA = <$fhA>) ) {
    				print "=======================================FICA Fini\n\n";
    				$LigneA = "x{FFFF}";
    				$EOFA = 1;
    			} else {
    				$NbA++;
    			}
    		} else {																				# $LigneA gt $LigneB
    			if (  ! $EOFB ) {
    				print "\nAjout dans FICB :\n";
    				print "LIGNEB=$NbB=$LigneB=\n";
    				if ( !($LigneB = <$fhB>) ) {
    					print "=======================================FICB Fini\n\n";
    					$LigneB = "x{FFFF}";
    					$EOFB = 1;
    				} else {
    					$NbB++;
    				}
    			}
    		}
    	}
    }
     
    close $fhA;
    close $fhB;
     
    $Date_Heure = date();
    print "\nTIME=$Date_Heure\n";
     
    exit;
     
    #==============================================================
    # Pour avoir les accents sur la console DOS
    # http://perl.developpez.com/faq/perl/?page=Terminal#AccentsDOS
    #==============================================================
    sub ActiverAccents {
    	my $encodage;
    	# Windows
      	if ( lc($^O ) eq 'mswin32') {
    		eval {
    			my ($codepage) = ( `chcp` =~ m/:\s+(\d+)/ );
    			$encodage = "cp$codepage";
    			foreach my $h ( \*STDOUT, \*STDERR, \*STDIN, ) {
    				binmode $h, ":encoding($encodage)";
    			}
    		};	
    	}
    	else {
    		$encodage = `locale charmap`;
    		eval {
    			foreach my $h ( \*STDOUT, \*STDERR, \*STDIN, ) {
    				binmode $h, ":encoding($encodage)";
    			}
    		};	
    	}
      return $encodage;
    }
     
    ###############
    # Date et Heure
    sub date {
      my $time = shift || time;    #$time par defaut vaut le time actuel
      my ( $seconde, $minute, $heure, $jour, $mois, $annee, $jour_semaine, $jour_annee, $heure_hiver_ou_ete )
        = localtime($time);
      $mois  += 1;
      $annee += 1900;
     
      # On rajoute 0 si le chiffre est compris entre 1 et 9
      foreach ( $seconde, $minute, $heure, $jour, $mois, $annee ) { s/^(\d)$/0$1/; }
      return "Le $jour/$mois/$annee à $heure:$minute:$seconde";
    }
     
    =pod
    sort -o sortedFile1 Montaigne_1.txt
    sort -o sortedFile2 Montaigne_2.txt
    NB :
    - Les fichiers sont triés sous Cygwin
    - A l'exécution on peut bien sûr les inverser
    - Inconvénient, les numéros de ligne correspondent aux fichiers triés et n'ont donc pas grand intérêt. Pour bien faire il faudrait ajouter les numéros de ligne en fin de ligne des fichiers avant le tri, pour pouvoir les récupérer ensuite...
    - La méthode est archaïque, comme moi, mais elle fonctionne, enfin j'espère

  17. #37
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Pour le fun, ma version d'un appareillage, provenant d'un module que j'ai écrit (et qui a un peu évolué depuis, mais, bon, cette version marche bien à ma connaissance):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
        my $ligne1 = <$IN1>;
        my $ligne2 = <$IN2>;
        die "One of the input files is empty\n" unless defined $ligne1 and defined $ligne2;
        chomp ($ligne1, $ligne2);
        while ( 1 ) {
            my $comparison = $sub_ref->($ligne1, $ligne2);
            if ($comparison > 0) {
                $nr_orphan2 ++;
                print $ORPH2 $ligne2, "\n";
                $ligne2 = <$IN2>;
                last unless defined $ligne2;
                chomp $ligne2;
            } else {
            if ($comparison < 0) {
                $nr_orphan1 ++;
                print $ORPH1 $ligne1, "\n";
                $ligne1 = <$IN1>;
                last unless defined $ligne1;
                chomp $ligne1;
            } else {
                print $OUT1 $ligne1, "\n";
                print $OUT2 $ligne2, "\n";
                $nr_common ++;
                $ligne1 = <$IN1>;
                $ligne2 = <$IN2>;
                last unless defined $ligne1 and defined $ligne2;
                chomp ($ligne1, $ligne2);
                }
            }
        }    
        print $ORPH2 $ligne2 and $nr_orphan2 ++ if defined $ligne2;
        print $ORPH2 $ligne2 and $nr_orphan2 ++ while $ligne2 = <$IN2>;
        print $ORPH1 $ligne1 and $nr_orphan1 ++if defined $ligne1;
        print $ORPH1 $ligne1 and $nr_orphan1 ++while $ligne1 = <$IN1>;
    Pour expliquer un peu, j'ai 2 fichiers triés à comparer en entrée ($IN1 et $IN2) et quatre fichiers en sortie:
    - $OUT1 et $OUT2: les enregistrements dont la clef est commune (mais dont le contenu peut néanmoins être différent sur d'autres éléments que la clef, d'où le besoin de deux fichiers distincts;
    - $ORPH1 et $ORPH2, les orphelins (enregistrements manquants d'un côté ou de l'autre).

    Je sais, c'est un peu compliqué, mais comme c'est une fonction dans un module générique, je dois essayer de prévoir tous les besoins possibles.

    L'autre ligne qui peut poser problème est celle-ci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    my $comparison = $sub_ref->($ligne1, $ligne2);
    Là encore, c'est un module générique, je cherche à prévoir tous les cas de figure. Donc, la fonction de comparaison entre deux lignes est passée en paramètre par le programme principal à la fonction remove_orphans sous la forme d'une référence à une fonction de rappel.

    S'il s'agit de comparer les lignes sur leur longueur totale, la valeur de $sub_ref peut être initialisée simplement comme suit dans le programme appelant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    $sub_ref = sub { return $_[0] cmp $_[1];}
    Cette fonction de rappel doit fonctionner de la même manière que les fonctions cmp ou <=> utilisées dans les tris, retourner 0 si les enregistrements sont considérés comme égaux, -1 ou +1 (ou n'importe quelle autre valeur positive ou négative) selon l'ordre dans lequel on veut les considérer.

    Si l'on désire que la comparaison se fasse seulement sur le premier champ d'un CSV (avec ";" comme séparateur), le programme appelant pourra définir la fonction de rappel comme suit (par exemple)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $sub_ref = sub {
         my ($rec1, $rec2) = @_;
         my $key1 = (split /;/, $rec1)[0];
         my $key2 = (split /;/, $rec2)[0];
         return $key1 cmp $key2;
    }
    (On pourrait écrire cela bien plus brièvement, éventuellement une seule ou deux lignes de code, mais ça deviendrait assez peu lisible, je préfère détailler le cheminement.)

    Bref, je sais, c'est un tout petit peu compliqué, mais l'avantage est que mon module est complètement générique et est exploitable pour tous les cas que j'ai eu à gérer depuis, parce que tous les détails de ce que l'on veut comparer lui sont passés en paramètres (j'avais essayé d'écrire des modules génériques auparavant, mais j'étais obligé de les adapter à chaque fois parce que les demandes changeaient trop; ici, c'est un peu plus compliqué, mais ça marche à tous les coups.) La fonction ci-dessus peut être considérée comme une fonction d'appareillage abstraite, elle est indépendante de ce que l'on vas comparer, puisque l'algorithme de comparaison lui est passé par le programme appelant.

    J'espère que je suis suffisamment clair, mais je suis tout disposé à expliquer ce qui pourrait rester obscur.

    Le module en question fait d'autres choses que la recherche des orphelins (recherche des doublons, vérification du tri, comparaison du contenu des enregistrements hors clé de comparaison, etc.), qui dépendent aussi de l'ordre des fichiers à comparer, mais toutes ses fonctions sont génériques (ou abstraites).

    Bien entendu, je suis prêt à fournir l'ensemble du code du module et de son (abondante) documentation (en anglais) à quiconque le demande. J'ai pris la précaution de l'écrire (presque) entièrement sur mon temps libre (en fait, entièrement sur mon temps libre, mais il est vrai que j'ai corrigé certains très petits bugs dans des cas bien particuliers lors d'utilisations professionnelles, je devrais donc peut-être remercier mon employeur, mais il ne m'a jamais demandé de développer ce module (et puis, à vrai dire, je n'ai pas vraiment d'employeur, je suis consultant indépendant), je le considère donc comme mon œuvre exclusive. La documentation précise:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    =head1 COPYRIGHT AND LICENSE
     
    Copyright 2013 by Laurent Rosenfeld
     
    This library is free software; you can redistribute it and/or modify
    it under the same terms as Perl itself.

Discussions similaires

  1. Comparaison et modification de deux fichiers volumineux
    Par roman67 dans le forum Shell et commandes GNU
    Réponses: 7
    Dernier message: 04/10/2008, 08h51
  2. Réponses: 1
    Dernier message: 03/10/2008, 16h07
  3. Comparaison de deux fichiers EXCEL
    Par meufeu dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 18/10/2006, 22h56
  4. comparaison de deux fichiers textes
    Par chmaichel dans le forum Delphi
    Réponses: 1
    Dernier message: 28/07/2006, 11h35
  5. [langage] Comparaison de deux fichiers
    Par perlgirl dans le forum Langage
    Réponses: 4
    Dernier message: 04/05/2005, 16h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo