OVH Cloud OVH Cloud

win32, binmode, NUL et fichier du registre

3 réponses
Avatar
dcd
Bonjour,

J'essaye de modifer une ligne d'un fichier .reg (obtenu avec exporter
depuis regedit). Pour cela je l'ouvre (dans le filehandle REG), je
copie chaque ligne dans un nouveau fichier (TMP) , avec une modif en
cas de match :

while ( $ligne = <REG>) {
if ( $ligne =~ /Log File/ ) {
print TMP "\"Log File\"=\"$newlog\"\n" ;
}
else {print TMP $ligne}
}

Ceci ne marche pas : il y a le probleme du caractere de fin de ligne.

En regardant la doc et les news j'obtiens cette solution avec binmode
et $/ :

#!perl
use warnings ;
use strict ;

open REG, "fic.reg" or die "Err 1" ;
open TMP, ">fic.tmp" or die "Err 2" ;

my $ligne ;
my $newlog="logfile.txt" ;

binmode TMP ;
$/ = "\015\012" ;

while ( $ligne = <REG>) {
chomp($ligne) ;

if ( $ligne =~ /LogFile/ ) {
print TMP "\"Log File Folder\"=\"$newlog\"\n" }
else {print TMP $ligne }
}

Dans un editeur le fichier créé à l'air correct, mais par contre il
n'y a jamais de match (et donc jamais de remplacement) .

Si j'ajoute un print de $ligne avant le if, je vois des caracteres NUL
presents entre chaque lettre. J'ai donc ajouté : $ligne =~ s/\0//g
apres le chomp.

Pour simplifier j'ai egalement supprimé le if : j'arrive alors à un
comportement etrange où ce qui s'affiche à l'ecran est correct mais où
le fichier créé ne contient que des codes illisibles !

En rajoutant à nouveau le if, c'est encore plus bizarre : le
remplacement a lieu mais à l'ecran comme dans le fichier la seule
ligne presente est celle remplacée . Je suppose que le reste est
constitué de caracteres illisibles, ou nul, ou ?

Bref je suis perdu entre les differents CR, LF, binmode, NUL et autres
manieres differentes de coder un fichier texte ! C'est pourtant
*simple* ce que je veux faire !

Si quelqu'un a compris quelque chose à cette histoire, merci de bien
vouloir m'aider !

Dav'

3 réponses

Avatar
DominiX
ici même:aarg a écrit
Bonjour,


salut


J'essaye de modifer une ligne d'un fichier .reg (obtenu avec exporter
depuis regedit). Pour cela je l'ouvre (dans le filehandle REG), je
copie chaque ligne dans un nouveau fichier (TMP) , avec une modif en
cas de match :

while ( $ligne = <REG>) {
if ( $ligne =~ /Log File/ ) {
print TMP ""Log File"="$newlog"n" ;
}
else {print TMP $ligne}
}

Ceci ne marche pas : il y a le probleme du caractere de fin de ligne.



quelle est ta definition de "Ceci ne marche pas" ?

que veux tu obtenir ? qu'obtient tu ?

-- dominix

Avatar
dcd
Salut !

Ce que je veux faire : remplacer une ligne d'un fichier .reg par une
ligne "à moi" , placer le resultat dans un nouveau fichier.

En rechargeant dans le registre ce fichier modifié je change le
repertoire de log par defaut d'une appli windows. Je n'utilise pas
TieRegistry car ce fichier m'est fournit par quelqu'un d'autre, qu'il
contient des clés bien complexes et que parser tout ça "à la main"
serait ... trop complexe / impossible à maintenir. Il est plus simple
pour moi de faire un appel externe à regedit /S sur le fichier.

Ce qui ne marche pas :

- Soit le fichier cible est illisible (carateres non ascii)

- Soit j'arrive à copier le fichier (ligne à ligne, donc) mais dans ce
cas impossible d'appliquer une regex valable sur une ligne. Ceci me
semble du à la presence du caractere NUL plusieur fois par ligne .
Effectuer un s///g ammene des resultats pour le moins aleatoires.

C'est sur windows 2000 avec perl 5.6 (activestate) .

Je me dit que le fichier .reg en question est peut etre corrompu, et
je vais donc re-essayer avec un nouveau fichier, mais tout cela me
laisse perplexe quand à l'utilisation de binmode et $ .

dav'
Avatar
jl_morel
Dans l'article , a
dit...

Ce que je veux faire : remplacer une ligne d'un fichier .reg par une
ligne "à moi" , placer le resultat dans un nouveau fichier.

[couic]

Ce qui ne marche pas :

- Soit le fichier cible est illisible (carateres non ascii)

- Soit j'arrive à copier le fichier (ligne à ligne, donc) mais dans ce
cas impossible d'appliquer une regex valable sur une ligne. Ceci me
semble du à la presence du caractere NUL plusieur fois par ligne .
Effectuer un s///g ammene des resultats pour le moins aleatoires.

C'est sur windows 2000 avec perl 5.6 (activestate) .



Sous Windows 2000 un fichier REG est écrit en UTF16, c'est pour cela que
vous avez plein de caractères NUL :-)

Il faut lire le fichier en mode binaire, convertir d'UTF16 en UTF8, traiter
la chaîne en UTF8 (Perl comprend UTF8) et reconvertir en UTF16 pour écrire
en mode binaire dans le fichier de sortie. Voir script plus bas.

Il s'agit en fait d'UTF16-LE (Little Endian). Pour l'entrée, pas de
problème : le fichier commence par les "Byte Order Marks" (FFFE). Par
contre, pour la sortie, la conversion par défaut est UTF16-BE; il faut
utiliser la méthode byteswap pour obtenir du LE.

Puisque le fichier d'entrée est lu en mode binaire il n'y a pas de notion
de ligne, d'où le mode "slurp" : tout le fichier est dans la chaîne $utf8.
Vous pouvez la découper en lignes avec split si c'est nécessaire. Pour les
tests j'ai utilisé une substitution simple sans caractère exotique comme é
è ç à...etc. Si vous avez à utiliser des caractères de code >128 il faut
les écrire/convertir en utf8 !

Je ne sais pas si le module Unicode::String est livré par défaut avec
Perl 5.6. Vous pouvez l'installer avec ppm dans mon dépot ppm :

http://www.bribes.org/perl/ppmdir.html

N.B. N'ayez pas de regrets de ne pas utiliser Perl 5.8 pour ce coup là.
Normalement on devrait pouvoir écrire directement :

open REG, "<:encoding(UTF16)", "avant.reg" or die $!;
open TMP, ">:encoding(UTF16-LE)", "apres.reg" or die $!;

et tout devrait être transparent. Malheureusement le "layer" se prend les
pieds dans le tapis avec les CR/LF sous Windows :-( bug connu.


#!/usr/bin/perl -w
use strict;
use Unicode::String qw/ utf16 utf8 /;

open REG, "< avant.reg" or die $!;
binmode REG;

open TMP, "> apres.reg" or die $!;
binmode TMP;
undef $/; # mode slurp

my $instr = utf16(<REG>); # lecture en UTF16

my $utf8 = $instr->utf8; # conversion en UTF8 pour Perl

$utf8 =~ s/Control/Kontrol/g; # manipulation de la chaîne UTF8

my $outstr = utf8($utf8);

$outstr->byteswap; # pour conversion UTF16-LE (Little Endian)
print TMP $outstr->utf16;

close TMP;
close REG;

__END__


HTH

--
J-L.M.