2014-09-18 2 views
0

Dans cet exemple, je voudrais modifier le fichier en place (sans tarte):remplacer une chaîne par une autre en place

# Initialise file 
open my $fh, '>', 'test'; 
print $fh 'I love apples'; 
close $fh; 

# Do the replacement 
open $fh, '+<', 'test'; 
s/love/hate/ while(<$fh>); 
close $fh; 

# Test content (should be 'I hate apples') 
open $fh, '<', 'test'; 
print while(<$fh>); 
close $fh; 

Malheureusement cet exemple ne fonctionne pas. Je trouve seulement cette solution laide:

# Do the replacement 
open $fh, '<', 'test'; 
my $out; 
my $changes; 
while(<$fh>) { 
    $changes += $_ =~ s/love/hate/; 
    $out.=$_; 
} 
if($changes) { 
    open $fh, '>', 'test'; 
    print $fh $out; 
} 
close $fh; 

Une meilleure solution?

Condition: je veux seulement toucher mon fichier s'il y a quelque chose à changer. Astuce: Je suis sur Windows/Cygwin :(

+0

Vous semblez rechercher, lire, imprimer. Ce sera plus moche que de lire tout le fichier en mémoire et de l'imprimer. – ikegami

+0

Est-ce encore le cas pour les gros fichiers? – nowox

+0

hein? Les aspects du code ne sont pas affectés par la taille du fichier de données. – ikegami

Répondre

2

À moins que la substitution a la même longueur, vous devez réécrire le fichier à partir du point de substitution avant d'insérer/effacer même une lettre dans ce

-i. option est généralement mis en œuvre en utilisant un fichier temporaire Tous vos modifications sont enregistrées dans un fichier temporaire qui est renommé à la fin du nom de fichier d'origine.

$ perl -i -pe's/a/bc/g' input1 
$ <input2 perl -pe's/a/bc/g' >output && replace output input2 
$ <input3 perl -pe's/a/bc/g' | sponge input3 

-à-dire,

while(<$fhorig>) { 
    $changed += $_ =~ s/love/haaate/; 
    print $fhtmp $_; 
} 
# close, fsync files.. 
rename $tmp, $orig if $changed; 

Si le fichier d'entrée est petit; vous pouvez effectuer des modifications en mémoire à la place sans le fichier temporaire. Votre code dans la question le fait.

Si la substitution a la même longueur alors vous pouvez mmap le fichier et faire les changements inplace. Windows et Unix supportent mmap. Il permet de travailler avec un fichier volumineux comme s'il s'agissait d'une chaîne de caractères ou de l'émuler en utilisant read/seek/write.

2

La documentation pertinente:

Dans votre cas, je vous recommande d'utiliser $INPLACE_EDIT comme modèle dans l'une des deux entrées de FAQ ci-dessus.

use strict; 
use warnings; 
use autodie; 

my $file = 'test'; 

# Initialise file 
open my $fh, '>', $file; 
print $fh <DATA>; 
close $fh; 

# Do the replacement 
local @ARGV = $file; 
local $^I = '.bak'; 
while (<>) { 
    s/love/hate/; 
    print; 
} 
unlink "$file$^I"; # Optionally delete backup 

# Test content (should be 'I hate apples') 
open $fh, '<', 'test'; 
print <$fh>; 
close $fh; 

__DATA__ 
I love apples 
I love oranges 
I love bananas 

Sorties:

I hate apples 
I hate oranges 
I hate bananas 

Note: si vous êtes sur Windows, vous devez spécifier une extension de sauvegarde pour -i. Par conséquent, je le fais toujours pour être compatible avec plusieurs plateformes.

+0

'$^I' viole: *" Exigence: je veux seulement toucher mon fichier s'il y a quelque chose à changer " * mais il semble que OP est d'accord avec ça. – jfs

+0

@ J.F.Sebastian bonne remarque et le prix vous revient. Mais la réponse du Miller est belle! – nowox

Questions connexes