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 :

[regexp] Remplacer une chaîne par une chaîne aléatoire


Sujet :

Langage Perl

  1. #1
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2010
    Messages : 18
    Points : 15
    Points
    15
    Par défaut [regexp] Remplacer une chaîne par une chaîne aléatoire
    Bonjour,

    J'ai un fichier texte texte.csv à valeurs séparées par des points-virgules :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    "Jean";"Martin";"0123456789";"0600000000";
    "Pierre";"Dupond";"987654321";"0611111111";
    Je cherche à remplacer toutes les chaînes de caractères et tous les chiffres par des chaînes aléatoires et des chiffres aléatoires. J'ai écrit le code suivant :

    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
     
    #!/usr/bin/perl -w
     
    if (!open(FIC,"$ARGV[0]")) {
    	print "impossible d'ouvrir $ARGV[0]";
    	exit(1);
    }
     
    my @t;
    my @colonne;
     
    while (defined($l=<FIC>)) {
    	@entree=split(/;/,$l);
    	$no_col=0;
    	foreach $cellule (@entree) {
    		$t[$.][$no_col]=$cellule;
    		$no_col++;
    	}
    }
     
    my @lettres = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" ); 
     
    for my $i (1..scalar(@t)-1) {
    	for my $j (0..$no_col-1) {
    		$_=$t[$i][$j];
    		s/[[:alpha:]]/$lettres[int(rand(scalar(@lettres)-1))]/g;
    		s/[0-9]/int(rand(9))/g;
    		$t[$i][$j]=$_;
    	}
    }
     
    for $i (1..scalar(@t)-1) {
    	for $j (0..$no_col-1) {
    		print "$t[$i][$j];";
    	}
    	print "\n";
    }
    J'obtiens la sortie suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    $ perl remplacement.pl texte.csv
    "qqqq";"jjjjjj";"int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))";"int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))";
    ;
    "jjjjjj";"eeeeee";"int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))";"int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))int(rand(9))";
    ;
    1. Les expressions régulières semblent ne pas évaluer les fonctions (ici, la fonction int(rand(9))). Y a-t-il un moyen de les forcer à le faire ?

    2. À l'intérieur d'une même expression régulière, la fonction rand() n'est calculée qu'une fois. Peut-on la forcer à se rafraîchir, par exemple en insérant le troisième terme de l'expression régulière au sein d'une boucle, quelque chose du type s/[[:alpha:]]/for my $k (0..length($t[$i][$j]) { ... }/g ? Mais je ne vois pas trop comment on pourrait faire...

    3. Pourriez-vous me donner une façon d'écrire ce programme, éventuellement plus concise, plus propre ou plus "perlish" ?

    4. Pourquoi Perl me met-il des "\0;" à chaque fois que je sors de la boucle indexée par $j ?

    5. J'aurais pu écrire scalar(@t)-1 de la façon suivante : $#t. Mais comment écrire le dernier indice de $t[$i] ? $#{$t[$i]} ?


    Merci d'avance,
    endreillie

  2. #2
    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'ai mis dans le code, un certain nombre de commentaires explicatifs.
    Si tu en veux plus, tu peux me les demander.

    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
    #!/usr/bin/perl -w
     
    # INDISPENSABLE
    use strict;
     
    # Utiliser la forme à 3 paramètres de open, et utiliser un filehandle scalaire (my)
    open my $FIC, "<", $ARGV[0] or
      die "impossible d'ouvrir $ARGV[0]: $!\n"; # Ajout de $! donnant la raison de l'erreur
     
    my @t;
    my @colonne;
    my @lettres = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" ); 
     
    # le defined n'est pas utile, en revanche, le my l'est (avec strict)
    while (my $l = <$FIC>) {
      my @entree = split(/;/, $l);
     
      foreach my $cellule (@entree) {
        # Il n'est pas utile de stocker, traiter et écrire le fichier dans trois boucles... autant tout faire ici
        # Pour executer du code dans la partie "remplacement" d'une regexp, il faut ajouter le flag /e
        # Il n'est pas nécessaire d'utiliser $_ pour appeler l'opération s///, il suffit d'utiliser l'opération =~
        $cellule =~ s/[[:alpha:]]/$lettres[int(rand(scalar(@lettres)-1))]/ge;
        $cellule =~ s/[0-9]/int(rand(9))/ge;
      }
      # Il n'est pas nécessaire de boucler avec un for pour parcourir un tableau, ou l'afficher
      print join ";", @entree;
    }
    On peut faire nettement plus court, bien sur. Si le sujet t'intéresse, je m'y penche volontiers.
    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

  3. #3
    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
    Un détail : dans les substitutions actuelles, le caractère "z" et le digit 9 ne sont jamais utilisés. Il faut légèrement modifier les limites hautes de rand pour qu'ils soient autorisés.
    (j'en suis à 5 lignes de code, dont la ligne "use strict" ; accessoirement, il est possible de gagner de réaliser cela en uniligne également à l'aide de seulement 2 instructions et les bonnes options).
    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

  4. #4
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2010
    Messages : 18
    Points : 15
    Points
    15
    Par défaut
    Bonjour,

    Citation Envoyé par Philou67430 Voir le message
    Un détail : dans les substitutions actuelles, le caractère "z" et le digit 9 ne sont jamais utilisés. Il faut légèrement modifier les limites hautes de rand pour qu'ils soient autorisés.
    Pourtant je croyais que rand(n) renvoyait un réel entre 0 et n. Ta remarque tiendrait-elle la "densité" de n dans [0,n] est nulle, donc que la probabilité que rand(n) renvoie n est nulle, donc que l'événement rand(n)=n est quasi-impossible, donc que 0 <= int(rand(n)) <= n-1 ?

    Dans ce cas suffit-il de mettre int(rand(10)) dans mon code ? Mais il faut peut-être quand même se protéger du cas où par miracle rand(10) renverrait 10, donc ça donnerait quelque chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    $randnb=rand(10);
    if ($randnb==10) {
        $randnb=9;
    }
    Suis-je dans le vrai ?

    Citation Envoyé par Philou67430 Voir le message
    (j'en suis à 5 lignes de code, dont la ligne "use strict" ; accessoirement, il est possible de gagner de réaliser cela en uniligne également à l'aide de seulement 2 instructions et les bonnes options).
    Je suis curieux de voir ça.

    Cordialement,
    endreillie
    Étudiant 1re année mathématiques-informatique

  5. #5
    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
    Citation Envoyé par endreillie Voir le message
    Pourtant je croyais que rand(n) renvoyait un réel entre 0 et n.
    ...
    Suis-je dans le vrai ?
    Non.
    Quand tu as un doute sur une fonction, il convient de regarder la documentation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    perldoc -f rand :
           rand EXPR
           rand    Returns a random fractional number greater than or equal to 0
                   and less than the value of EXPR.  (EXPR should be positive.)
    Dans ce contexte, rand(10) retournera toujours une valeur de l'intervalle [0, 10[ et int(rand(10)) retournera un chiffre de 0 à 9.
    Je suis curieux de voir ça.
    Sous forme de script :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #!/usr/bin/perl -w
     
    # INDISPENSABLE
    use strict;
     
    # Inutile d'ouvrir les fichiers en paramètre du script (en utilisant l'opérateur <>)
    while (<>) {
      s/[[:alpha:]]/chr(ord("a")+int(rand(26)))/ge;
      s/[0-9]/int(rand(10))/ge;
      print;
    }
    Sous forme d'utiligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -pe 's/[[:alpha:]]/chr(ord("a")+int(rand(26)))/ge;s/[0-9]/int(rand(10))/ge;' texte.csv
    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

  6. #6
    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 rand(10), essaye cet uniligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -e 'while (int(rand(10)) != 10) {} ; print "rand(10) = 10\n"'
    S'il te rends la main, j'aurais dit une bêtise (et la doc aussi)
    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

  7. #7
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2010
    Messages : 18
    Points : 15
    Points
    15
    Par défaut
    La doc précise bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    rand    Returns a random fractional number greater than or equal to 0
                   and less than the value of EXPR.  (EXPR should be positive.)
    Je n'ai jamais fait de mathématiques en anglais mais j'imagine qu'ils veulent dire qu'ici less signifie strictement inférieur.

    Il est bien rigolo ton uniligne, si on veut rester un siècle à contempler une ligne vide sur un terminal. Je serais bien curieux de savoir au bout de combien de temps on a ne serait-ce qu'une chance sur deux de voir apparaître un décimal précis. Ton dernier post serait-il une boutade ?

    Merci de m'avoir aidé. Je reposte prochainement une fois que j'aurai adapté le code que tu as corrigé à l'usage que je veux en faire (produire, à partir de mon carnet d'adresses en CSV, un pseudo-carnet d'adresses insignifiant que je pourrai poster avec le code que j'essaie désespérément d'écrire pour fusionner les doublons).

    Cordialement,
    endreillie

  8. #8
    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
    Citation Envoyé par endreillie Voir le message
    Je n'ai jamais fait de mathématiques en anglais mais j'imagine qu'ils veulent dire qu'ici less signifie strictement inférieur.
    Cela signifie "inférieur", et comme en français, l'opérateur "inférieur" (<) signifie mathématiquement "strictement inférieur"
    Il est bien rigolo ton uniligne, si on veut rester un siècle à contempler une ligne vide sur un terminal. Je serais bien curieux de savoir au bout de combien de temps on a ne serait-ce qu'une chance sur deux de voir apparaître un décimal précis. Ton dernier post serait-il une boutade ?
    On peut le voir ainsi, et je m'en excuse si c'est ainsi que tu l'as compris.

    J'explique son fonctionnement : le programme boucle jusqu'à ce qu'une valeur retournée par int(rand(10)) soit égale à 10 (note le int() qui réduit à 10 ou 11 valeurs le nombre de valeurs retournées).
    Comme le programme ne rend jamais la main, int(rand(10)) ne retourne jamais 10 (bien sûr, pour le confirmer, il faudrait attendre à l'infini).

    Une autre manière de comprendre ma boutade est de voir qu'un uniligne est souvent utile pour comprendre le fonctionnement d'une fonction ou d'un opérateur.
    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

  9. #9
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2010
    Messages : 18
    Points : 15
    Points
    15
    Par défaut
    J'explique son fonctionnement : le programme boucle jusqu'à ce qu'une valeur retournée par int(rand(10)) soit égale à 10 (note le int() qui réduit à 10 ou 11 valeurs le nombre de valeurs retournées).
    Comme le programme ne rend jamais la main, int(rand(10)) ne retourne jamais 10 (bien sûr, pour le confirmer, il faudrait attendre à l'infini).
    J'ai bien compris le fonctionnement de ton programme, simplement en supposant que rand(10) renvoie un nombre n tel que 0<=n<=10 (ce qui paraît faux au vu de la doc), pour que int(rand(10)) retourne 10, il faudrait que rand(10) retourne 10.0000000000000, ce qui est quasi impossible.

    À mon sens, si ton programme rend la main, alors 10 fait partie des valeurs que rand(10) peut retourner ; mais ce n'est pas parce que le programme ne rend pas la main que l'on peut en déduire que rand(10) ne retourne jamais 10.

    Ce programme non plus ne rendra pas la main :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -e 'while (rand(10)!=5.346) {} print "rand(10)=5.346\n";'
    et pourtant, 5.346 fait bien partie de [0,10[.

    J'espère que je me suis bien exprimé, et que tu pourras me corriger si je me trompe.

    Cordialement,
    endreillie

  10. #10
    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'avais précisé deux choses dans mon exposé :
    - la première est qu'il faudrait attendre à l'infini pour être bien sur que 10 ne sera jamais retourné.
    - la deuxième était que int() réduisait à 10 ou 11 valeur possibles.

    Dans ce cas, j'ai effectivement omis de préciser que s'il y avait 11 valeurs possibles (comprendre que si rand(10) pouvait retourner 10), les 11 valeurs ne seraient pas équiprobables. On n'est donc contraint d'attendre à l'infini pour confirmer que rand(10) ne retournera jamais 10.
    Mon argument ne valait donc effectivement pas un clou.

    Cela dit, la doc est explicite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    perldoc -f rand
      ...
                   Apply "int()" to the value returned by "rand()" if you want
                   random integers instead of random fractional numbers.  For
                   example,
     
                       int(rand(10))
     
                   returns a random integer between 0 and 9, inclusive.
      ...
    Encore désolé.
    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

Discussions similaires

  1. [XL-2007] [FORM] remplacer la valeur d'une cellule par une autre
    Par coldavires dans le forum Excel
    Réponses: 7
    Dernier message: 21/01/2010, 22h34
  2. Remplacer une lettre par une autre dans une cellule
    Par Philippe76 dans le forum Excel
    Réponses: 1
    Dernier message: 09/01/2010, 21h26
  3. [PROC] Remplacer un point par une virgule dans une Proc Tabulate
    Par PAULOM dans le forum SAS Base
    Réponses: 2
    Dernier message: 20/08/2009, 08h36
  4. [Toutes versions] Remplacer "#N/A" par une valeur par défaut
    Par canary dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 10/05/2009, 11h23
  5. [RegEx] Remplacer dernière occurence d'une chaine par une autre
    Par webjoujou dans le forum Langage
    Réponses: 3
    Dernier message: 12/11/2008, 17h58

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