Problème perl

Fermé
bramble - 7 avril 2008 à 00:17
 bramble - 13 avril 2008 à 20:50
Bonjour,
je débute en programmation et mon code ne fonctionne pas.
quelqu'un peut-il m'expliquer mon erreur?
voici le code:
#!/usr/bin/perl

$chaine="EDI0A4S1.VEC";
$i=0;

chomp($chaine);

if ($chaine)		#on vérifie la présence du fichier
	{
	open(FILE,"$chaine") || die "Problème à l'ouverture : $!";
		while(<FILE>)				#ligne 12: on ouvre le fichier et on le lit ligne à ligne
			{
			$i ++;
			$ch1=substr($_, 0, 9); 	
			if ($ch1 eq "ATVST 1:z")	#dés qu'on trouve la chaine de caractère ATVST 1:z
				{
				print "ATVST 1:z decelé à la ligne : $i\n";
				last;			#on sort de la boucle pour garder dans $i le n° de ligne
				}
			open(FILE2,">>$chaine") || die "Problème à l'ouverture : $!";	
                                                                                                 #on reouvre le fichier pour pouvoir 
				while(<FILE2>)						#traiter des lignes situées avant la ligne i
					{
					$j=0;
					$j ++;
					if ($j == $i-8){$ch2=substr($_, 8);}			
#on récupère le nom d'un bloc à la ligne i-8
					elsif ($j == $i-2){s/ATCSN01:1/ATCSN01:0/;}		
#et on modifie des lignes dont la position est connue par rapport à "ATVST 1:z"
					elsif ($j == $i-1){s/ATPCP22:EDI0A4;SeSD;ATT;TEX_id//;}	

					elsif ($j == $i){s/ATVST 1:z//;last;}
					}
			close FILE2 || die "Problème à la fermeture : $!";
			open(FILE3,"$chaine") || die "Problème à l'ouverture : $!";	
#on ouvre encore le fichier pour pouvoir remonter
				while(<FILE3>)						
					{
					$j=0;
					$j ++;
					$ch3=substr($_, 28);
					if ($ch3 eq $ch2){last;}			
#on identifie la ligne et on sauvegarde sa position dans $j
					}						
			close FILE3 || die "Problème à la fermeture : $!";
			open(FILE4,">>$chaine") || die "Problème à l'ouverture : $!";
				while(<FILE4>)
					{
					$k=0;
					$k ++;
					if ($k == $j+1){s/ATCSN01:0//;}			
#on traite des lignes dont la position est connue par rapport à la chaine récupérée dans $ch3
					elsif ($k == $j+2){s/QACSN 1:0//;}		
					elsif ($j == $j+4){s/RTYSA 3:FEA//;}
					}
			close FILE4 || die "Problème à la fermeture : $!";
			print "Bloc supprimé : $ch2\n";				
#on affiche le nom du bloc supprimé et on remonte à la ligne 12 
#tant que "ATVST 1:z" est présent dans le fichier
			}							

	close FILE || die "Problème à la fermeture : $!";
	}
else {print "le fichier n'existe pas\n";}
A voir également:

11 réponses

lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
7 avril 2008 à 09:07
Salut,

je débute en programmation et mon code ne fonctionne pas.

En ce cas, j'aimerais bien que tu affiche le contenu de ce fameux fichier et d'expliquer ce que tu veux obtenir comme résultat.

Ouvrir 4 fois un handle de fichier vers le même fichier, je ne pense pas que c'est le top ;-)
Merci
0
merci de m'avoir répondu.
oui je pense bien que ce n'est pas tres propre mais je ne vois pas comment faire autrement.
je veux en fait remonter de 8 lignes dans le fichier à partir d'une chaine de caractère que connait pour en recuperer une autre.
le seul moyen que j'ai trouvé c'est de scanner mon fichier jusqu'à trouver la première chaine de caractère, de fermer le fichier et de le reouvrir pour le scanner depuis le debut. comme je connais la ligne i de ma première chaine de caractère, je peux aller recupérer la deuxième à la ligne j=i-8.
y a t-il une autre façon de rescanner le fichier depuis le debut?
depuis mon premier post j'ai continué a chercher et j'ai tout décomposé. la premiere partie fonctionne:
#!/usr/bin/perl
use strict;
my $chaine="EDI0A4S1.VEC";
my $i=0;
my $j=0;
my $ch1;
my $ch2;
chomp($chaine);

if ($chaine)		
	{
	open(FILE,"< $chaine") || die "Problème à l'ouverture : $!";
		while(<FILE>)				
			{
			$i ++;
			$ch1=substr($_, 0, 9); 	
			if ($ch1 eq "ATVST 1:z")	
				{
				print "ATVST 1:z décelé à la ligne : $i\n";
				last;			
				}
			}
	close FILE || die "Problème à la fermeture : $!";
	open(FILE2,"< $chaine") || die "Problème à l'ouverture : $!";	
		while(<FILE2>)						
			{
			$j ++;
			if ($j == $i-8){$ch2=substr($_, 8);last;}			
			
						
			}
	close FILE2 || die "Problème à la fermeture : $!";
	print "chaine2: $ch2\n";	
	}
	
else {print "le fichier n'existe pas\n";}


est-il possible de coder ça plus proprement?
merci d'avance.
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
7 avril 2008 à 11:42
est-il possible de coder ça plus proprement?

oui, mais j'aurais besoin de ton fichier que tu traites.
0
il est long, je ne vais mettre que la partie concernée:

RTYSA 3:FEA
RIDSA 8:Fea_6773

SCPCP27:EDI0A4;SeSD;OBJ;SUBDFISC_id
CM1CC 0:
CM2CC 0:
REFCC 0:
ATCSN01:1
ATPCP22:EDI0A4;SeSD;ATT;TEX_id
ATVST 1:z
QACSN 1:0


on a ici un bloc de type fea, son identifiant est fea_6773.
ce bloc en est un parmi plein d'autres (le fichier fait 23000 lignes).
ce que je cherche à faire c'est identifier tous les blocs qui ont un z après "ATVST 1:" et recuperer leur nom, ici:"Fea_6773".
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
7 avril 2008 à 14:41
Re,

le fichier fait 23000 lignes
ce n'est rien 23000

j'ai vu des fichier avec 68 000 000 lignes

Mets le fichier sur cjoint.com, je vais regarder ce soir

j'ai déjà une idée pour optimiser le traitement, mais j'ai besoin d'un fichier réel
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
merci pour ta réactivité.
voici le lien cjoint:
https://www.cjoint.com/?ehoTk6whK5
si ça peut t'aider voici le traitement complet qu'il faut appliquer au fichier:
Il faut commencer par rechercher la chaine de caractère "ATVST 1:z", la supprimer puis supprimer celle qui est au dessus (ATPCP22:EDI0A4;SeSD;ATT;TEX_id) et enfin replacer le 1 de "ATCSN01:1" par 0.
Ceci fait il faut remonter de 6 lignes, récuperer l'identifiant "Fea_6773" qui se trouve toujours apres "RIDSA 8:"
ça, c'est la premiere étape. la deuxieme c'est de rechercher depuis le début du fichier (car ce qu'on cherche se trouve forcément avant le bloc qu'on vient de traiter) l'identifiant qu'on a récupéré (ici:"Fea_6773") à la fin de la chaine "FTPCP28:EDI0A4;SeSPA_S1;FEA;Fea_6773" et de supprimer tout le bloc fea qui suit à savoir pour cet exemple: de la ligne:RTYSA 3:FEA(ligne 9119) à QACSN 1:0 (ligne 9153).

Lorsque ceci est fait on recommence tant que la chaine:"ATVST 1:z" existe dans le fichier.


J'ai besoin de ce code pour ne plus avoir a faire ces operations à la main qu me prennent un temps fou. cela dit je souhaite avant tout apprendre et je ne te demande pas de me pondre le code tout cuit...
si déjà tu pouvais m'expliquer comment tu voit la solution et surtout ce qui ne va pas dans ce que j'ai fait precedemment ce serait génial!
merci d'avance
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
8 avril 2008 à 09:26
Salut,

voici pour la 1ère partie (vérifie si le résultat est bon)
#!/usr/bin/perl
use strict;use warnings;

open LIRE,"EDI0A4S1.VEC"
    or die "E/S : $!\n";

my($atvst,$tex_id,$atcsn,$ftpcp);

$atvst='ATVST 1:z';
$tex_id='ATPCP22:EDI0A4;SeSD;ATT;TEX_id';
$atcsn='ATCSN01:1';
$ftpcp='FTPCP28:EDI0A4;SeSPA_S1;FEA;';

undef $/;
my $fic=<LIRE>;

$fic =~s/$atcsn\n$tex_id\n$atvst/ATCSN01:0=====\n/sg;

my @fea = $fic =~ /\n^RIDSA 8:(Fea_.*?)\n^.*?\n^.*?\n^.*?\n^.*?\n^.*?\n^ATCSN01:0===/mg;
print $fic; # ATVST 1:z + ligne au dessus supprimés et ATCSN01:1 mis à zéro
print "@fea\n"; " capture de Fea

__END__
0
merci,
je ne comprends pas tout, il faut que je prenne le temps de voir ça et là je suis en deplacement, je rentre demain. je m'en occupe demain soir et je te tiens au courant dans la foulée.
juste un détail, à la derniere ligne, ce ne serait pas plutot un # avant "capture de Fea"? ;-)
mmmmh, ce language a l'air d'être plein de ressources, je m'en lèche les babines d'avance...
à demain.
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
8 avril 2008 à 21:02
Salut,

ce ne serait pas plutot un # avant "capture de Fea"? ;-)
c'était un test pour voir si tu suis ;-))

non, je plaisante, c'est une erreur de frappe
c'est #, bien vu ;-)
0
bien le bonsoir,
ça marche très bien.
j'ai trois questions par rapport à ce que tu as fait:
-dans les options des expression regulieres, tu mets s sur la premiere et m sur la deuxieme. pourquoi?
dans les deux cas il ya des \n qui indiquent les retours à la ligne et je ne voit pas bien l'interet de specifier si on considère l'expression sur une ou plusieurs lignes.
-on est obligé de laisser les ===== pour identifier les Fea qui ont été modifiés et on les supprime juste avant d'ecrire le fichier de sortie. c'est ça?
-ne pourrait on pas ecrire
my @fea = $fic =~ /\n^RIDSA 8:(Fea_.*?)\n(^.*?\n){5}^ATCSN01:0===/mg;

au lieu de
my @fea = $fic =~ /\n^RIDSA 8:(Fea_.*?)\n^.*?\n^.*?\n^.*?\n^.*?\n^.*?\n^ATCSN01:0===/mg;

du coup pour le dernier traitement ça simplifierait l'expression reguliere vu qu'il y a 34 lignes.
par exemple, ceci est-il correct?:
foreach $i (@fea) {$fic =~s/^FTPCP28:EDI0A4;SeSPA_S1;FEA;($i)\n$atcsn\n$qacsn\n(^.*?\n)\{34\}$qacsn/^FTPCP28:EDI0A4;SeSPA_S1;FEA;($i)\n$atcsn\n$qacsn\n/g;}

je n'ai pas le temps de le tester ce soir. prochaine edition demain soir.
en tout cas merci pour tout tu m'enleves une fière chandelle du pied!
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
10 avril 2008 à 00:15
Salut,

1. en mode slurp je peux capturer un fichier dans une variable scalaire
Comme tu as besoin de supprimer une chaine ainsi que celle qui est au dessus et encore modifier une 3 ème qui se trouve au dessus de tout les deux, j'ai préfére tout simplement de lire la chaîne et de faire la modification de la chaine qui suit les 2 autres que tu veux enlever

Pour ce cas l'utilisation de /s est superflue y compris celle de /m (c'était pendant les tests que j'ai fait et ensuite je n'ai plus l'enlever)

Dans le 2ème cas, /m est nécessaire pour permettre de reconnaître un début de ligne à l'intérieur de la chaîne

2. les === je l'ais mis juste pour permettre la vérification des changement, donc tu n'est pas obligé de les utiliser pour la suite

3. tu pourras l'écrire comme ça
my @fea = $fic =~ /\n^RIDSA 8:(Fea_.*?)\n(?:^.*?\n){5}^ATCSN01:0===/mg;


reguliere vu qu'il y a 34 lignes.
s'il s'agit toujours de 34 lignes alors les choses semblent être plus facile à traiter

je n'ai pas testé mais ta dernière regex peut être simplifiée
$fic =~s/^(FTPCP28:EDI0A4;SeSPA_S1;FEA;$i\n$atcsn\n$qacsn\n)(?:^.*?\n){34}$qacsn/$1/g;
je regarderai demain




0
bonsoir,
voila ça marche, je te remercie pour cette aide très instructive. sans toi je n'aurais jamais pu faire ça!
voici ce que j'ai fait, tu remarquera que la deuxième expression reguliere ne correspond pas à la recherche que j'avais decrite dans les messages precedents. en fait je m'etais un peu trompé mais ça ne change pas grand chose...
par contre je revient à la charge avec les =====, je pense qu'il faut les utiliser car lorsque tu recuperes les fea dans le tableau, vu qu'il n'ya plus de z pour identifier les blocs, il les recupere tous...

#!/usr/bin/perl
use strict;use warnings;

open LIRE,"EDI0A4S1.VEC"  or die "E/S : $!\n";
open RESULTAT,">resultat.txt" or die "E/S : $!\n";

my($atvst,$tex_id,$atcsn,$ftpcp,$qacsn,$rtysa,$i);

$atvst='ATVST 1:z';
$tex_id='ATPCP22:EDI0A4;SeSD;ATT;TEX_id';
$atcsn='ATCSN01:1';
$ftpcp='FTPCP28:EDI0A4;SeSPA_S1;FEA;';
$qacsn='QACSN 1:1';
$rtysa='RTYSA 3:FEA';

undef $/;
my $fic=<LIRE>;

$fic =~s/$atcsn\n$tex_id\n$atvst/ATCSN01:0=\n/sg;

my @fea = $fic =~ /\n^RIDSA 8:(Fea_.*?)\n(?:^.*?\n){5}^ATCSN01:0=/mg;
foreach $i (@fea) {$fic =~s/$rtysa\nRIDSA 8:(Fea_.*?)\n(?:^.*?\n){50}FTPCP28:EDI0A4;SeSPA_S1;FEA;($i)\nATCSN01:0\nQACSN 1:0//mg;}
$fic =~s/ATCSN01:0=/ATCSN01:0\n/g;
print RESULTAT $fic; # ATVST 1:z + ligne au dessus supprimés et ATCSN01:1 mis à zéro
print "@fea\n"; # capture de Fea

__END__


voila, maintenant il ne me reste plus qu'a faire une boucle pour pouvoir traiter tous les fichiers qui sont dans un repertoire, renommer les originaux en .old et donner leur nom aux fichiers créés. je pense que je vais pouvoir y arriver.
merci pour tout.
matthieu
0
bonsoir,
comme la discussion n'est pas passée en resolue, j'en profite pour poser une petite question.
j'ai bien reussi a faire fonctionner le code que lami20j m'a proposé mais un petit problème se pose sur certains fichiers.
déjà, voici le code qui liste les fichiers du repertoire courant et applique le meme traitement sur chaque fichier puis edite un petit rapport pour indiquer les objets traités:
#!/usr/bin/perl

use strict;use warnings;


my ($fichiers,@fichiers,$repertoire,$i,$j,$fic,$num_planche);

my $atvst='ATVST 1:z';

my $atcsn='ATCSN01:1';

my $qacsn='QACSN 1:1';

my $rtysa='RTYSA 3:FEA';

	

@fichiers = <*.VEC>;
foreach $i (@fichiers) 
	{
	
	$num_planche = $i; 				# $i contient le nom du fichier
	$num_planche=~s/EDI0(.*)S1\.VEC/$1/;		# $num_planche contient le numero de la planche

	
	open (LIRE,"$i")  || die "lecture : $!\n";
	rename("$i","$i.old") || die ("pb pour renommer les fichiers \n");
	open RESULTAT,">>$i" or die "ecriture : $!\n";
	open RAPPORT, ">>rapport.txt";

	undef $/;

	$fic=<LIRE>;



	$fic =~s/$atcsn\nATPCP22:EDI0$num_planche;SeSD;ATT;TEX_id\n$atvst/ATCSN01:0=\n/g;



	my @fea = $fic =~ /\n^RIDSA .*?:(Fea_.+?)\n(?:^.*?\n){5}^ATCSN01:0=/mg;

	foreach $i (@fea) {$fic =~s/^.*?\n$rtysa\nRIDSA .*?:(Fea_.+?)\n(?:^.*?\n){50}FTPCP.*?:EDI0$num_planche;SeSPA_S1;FEA;($i)\nATCSN01:0\nQACSN 1:0\n.*?//mg;}

	$fic =~s/ATCSN01:0=\n.*?\n/ATCSN01:0\n/g;


	print RESULTAT $fic; 

	print RAPPORT "sur la planche $num_planche, les objets z ont été supprimés sur les fea suivants:\n@fea\n"; 
	
	print "i : $i\n";
	print "numero de planche: $num_planche\n";
	print "fea reperes: @fea\n";

	}

__END__

sur certains fichiers ça fonctionne tres bien et sur d'autres pas. en fait il ne trouve pas la chaine de caractere recherchée et la seule difference que je vois entre les fichiers est la longueur de l'identifiant fea, par exemple dans le fichier de test on avait Fea_6773 et dans certains autres on pourra avoir Fea_650 ou Fea_25. j'ai donc modifié un peu l'expression reguliere pour cette identification mais ça ne change rien. voici donc deux fichiers, avec le 1 le code fonctionne et pas avec le 2:
1: https://www.cjoint.com/?enuUK2IgP1
2: https://www.cjoint.com/?enuVrlrKUF
si quelqu'un a une petite idée d'où vient le probleme...
merci d'avance.
0