CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Advanced:
Problem replacing only 1 peice of information in a text file! Please help.

 



CWDSolutions
New User

Apr 28, 2002, 3:22 AM

Post #1 of 4 (2790 views)
Problem replacing only 1 peice of information in a text file! Please help. Can't Post

Hello everyone,

I have a problem and I cannot seem to fix it. What I am
trying to do is open a text file which contains the follwing
lines:

OLDTHING=NEWTHING
OLDTHING2=NEWTHING2
OLDTHING3=NEWTHING3

And so on. Now what the script is supossed to do is read
in this file then open another file on the system and do
a search and replace according to the list above.

If it find OLDTHING it will replace it with NEWTHING same
for OLDTHING2 and so on.

What is happening it skips every other line in the file it
is seearching and it moves the text to the next line.

Now the search file may look like this:

This is the text and I want to replace 154.25 with 125.25

or may look like so:
154.25:125.25

And only need to replace the first same as above. Here
is the code I am using now we get the file name from
user input.


Code
 #!/usr/bin/perl  
#

# Enter the name of the file with the stuff to change
my $chglist = "changelist";

# lets promtpt for a file name
print "Enter file name: ";
chomp($in = <STDIN>);

if($in ne "") {
open(CHGFILE, $chglist) or die "Can't open $chglist: $!\n";
while(<CHGFILE>) {
# Here we get all the stuff and add them to easy to use variables
($oldstuff, $newstuff) = split("=", <CHGFILE>);
foreach ($_) {
$old = $in;
$new = "$file.tmp.$$";
$bak = "$file.orig";

open(OLD, "< $old") or die "can't open $old: $!";
open(NEW, "> $new") or die "can't open $new: $!";

# Correct typos, preserving case
while (<OLD>) {
s/$oldstuff/$newstuff/i;
(print NEW $_) or die "can't write to $new: $!";
}

close(OLD) or die "can't close $old: $!";
close(NEW) or die "can't close $new: $!";

rename($old, $bak) or die "can't rename $old to $bak: $!";
rename($new, $old) or die "can't rename $new to $old: $!";
}
}
}else{
print "Enter file name: ";
chomp($in = <STDIN>);
}


Thanks in advance.

Regards,
Ray


(This post was edited by CWDSolutions on Apr 28, 2002, 3:23 AM)


rGeoffrey
User

Apr 28, 2002, 10:30 PM

Post #2 of 4 (2776 views)
Re: [CWDSolutions] Problem replacing only 1 peice of information in a text file! Please help. [In reply to] Can't Post

You probably want to do a 'chomp' somewhere near this line...


Code
		($oldstuff, $newstuff) = split("=", <CHGFILE>);


Also you open and completely copy OLD to NEW each time through the loop and rename the files. Might it be better to read CHGFILE once storing the replacement values in an array of arrays and then open OLD exactly one time and for each line loop through the replacements before printing it?


CWDSolutions
New User

Apr 29, 2002, 12:38 AM

Post #3 of 4 (2774 views)
Re: [rGeoffrey] Problem replacing only 1 peice of information in a text file! Please help. [In reply to] Can't Post

Hello,

Thanks for your reply. I figured a way to do it differently.
The way I decided was to search a complete directory
and change any instance of the needed change in all files
while creating a backup of the old file. Here is what I did
if you can see where this can be improved let mw know:


Code
$iplist = "../iplist.txt"; 

$superuserpath = "/home/www/ipchanger/newone/";

open (CNG,"$iplist") || die "No Change List";
#Suck dem changes list is $changelist[x][0]=old ip $changlist[x][1]=new ip
$i=0;
while (<CNG>) {
@tmp=split(/=/);
$changelist[$i][0]=$tmp[0]; #Easy reading
$changelist[$i][1]=$tmp[1];
$changelist[$i][0]=~tr/\n//d; # Strip Out The HRT
$changelist[$i][1]=~tr/\n//d;
print "$i - $changelist[$i][0] will be $changelist[$i][1]\n";
$i++;
}

close (CNG);
$changecount=$i-1; #Lets not do the blank at the end
print "Kewl Stuff, I found $i changes to do...\n";

opendir(DIR,"$superuserpath") || die "Can't open superuser path";
do {
$aname=readdir(DIR);
# if (!-d $aname) {$aname="ipchanger.pl";}
if (-d $aname) {
$aname = "xxxxxx";
print "found Directory";
}
if ($aname ne "." && $aname ne ".." && $aname ne "" && $aname ne "ipchanger.pl" && $aname ne "xxxxxx"){
open (MOL,"$aname") || die "Cant open the file $aname - NO FUN";
local $/; # Disable Filepointer
$molestme=<MOL>; # Ever seen spaceballs? "Suck Suck Suck"
close (MOL);
open (FIL,">./backup/$aname\.bak") || die "Cant Write Output";
print FIL $molestme;
close (FIL);
$filesize=length($molestme);
print "I have found a file $aname with $filesize Bytes to pillage in it.\nAnd Away We GOOOO \n";
for $i(0..$changecount){
# for easy reading
$was=$changelist[$i][0];
$is=$changelist[$i][1];
# MAGIK SPOT --
$molestme=~s/$was/$is/g;
print "$was is now $is\n";
}
open (FIL,">$aname") || die "Cant Write Output";
print FIL $molestme;
close (FIL);
}
} while $aname ne "";
close (DIR);


This was done with the help of a friend off the boards. But
seems to work well other then the error about not being
able to re-write the directory.

Thanks for your replay.

Regards,
Ray


rGeoffrey
User

Apr 29, 2002, 9:59 AM

Post #4 of 4 (2767 views)
Re: [CWDSolutions] Problem replacing only 1 peice of information in a text file! Please help. [In reply to] Can't Post

Here is what I have come up with. I did not actually write out the files and have left those 6 lines commented out as I have a different environment than you and it was not really the point of the exercise. You will also want to throw out my strange if (0) where I set my directory names without being forced to comment yours out.


Code
#!/usr/local/bin/perl 

use strict;

my ($iplist, $superuserpath, $sourcedir, $donedir);

if (0) {
#Directories listed in the original post
$iplist = "../iplist.txt";
$superuserpath = "/home/www/ipchanger/newone/";
} else {
#Directories used while testing in a different environment
$iplist = "iplist.txt";
$superuserpath = "samples";
$sourcedir = "samples";
$donedir = "output"
}

#Suck dem changes list is $changelist[x][0]=old ip $changlist[x][1]=new ip

my @changelist;
my $i=0;
open (CNG, $iplist) || die "No Change List, $!";
while (<CNG>) {
chomp;
my @tmp = split(/=/);
push (@changelist, \@tmp);
print "$i - $tmp[0] will be $tmp[1]\n";
$i++;
}
close (CNG);

print "Kewl Stuff, I found ", scalar (@changelist), " changes to do...\n";

#These are protected files that are not to be manipulated each name will be
#a key in the hash so we can see if the key exists. The value is irrelevant.
my %protected = map { $_ => 1 } qw (. .. ipchanger.pl);

opendir(DIR, $sourcedir) || die "Can't open superuser path, $!";
while (my $aname=readdir(DIR)) {
if (-d "$sourcedir/$aname") {
print "\nfound Directory '$aname'\n";
} elsif (exists ($protected{$aname})) {
print "\nfound a protected file '$aname'\n";
} else {
open (MOL,"$sourcedir/$aname") || die "Cant open the file $sourcedir/$aname - NO FUN, $!";
local $/; # Disable Input Record Separator
my $molestme=<MOL>; # Ever seen spaceballs? "Suck Suck Suck"
close (MOL);

# open (FIL,">./backup/$aname\.bak") || die "Cant Write Output, $!";
# print FIL $molestme;
# close (FIL);

my $filesize=length($molestme);
print "\nI have found a file $aname with $filesize Bytes to pillage in it.\nAnd Away We GOOOO \n";

foreach my $pair (@changelist) {
my $was =$pair->[0];
my $is =$pair->[1];
# MAGIK SPOT --
my $ct = ($molestme=~s/$was/$is/g);
print "$was is now $is, changed $ct times\n" if ($ct);
}

# open (FIL,">$aname") || die "Cant Write Output, $!";
# print FIL $molestme;
# close (FIL);
}
}

close (DIR);


Now to go over several of the changes...

It is now 'use strict' safe as I have chased down all the variables and added my.


Code
open (CNG, $iplist) || die "No Change List, $!";  
while (<CNG>) {
chomp;
my @tmp = split(/=/);
push (@changelist, \@tmp);
print "$i - $tmp[0] will be $tmp[1]\n";
$i++;
}


By declaring 'my @tmp' inside the loop, we get a new one each time. And then we can push a pointer to the new small array of two elements to the end of the big array changelist. Thus we get our array of arrays and never have to use subscripts. $i was kept only for the print statement. And I chomped before the split to avoid the two regular expressions, although only one should have been needed as the part before the equals should not have had one.


Code
my %protected = map { $_ => 1 } qw (. .. ipchanger.pl); 
#....
} elsif (exists ($protected{$aname})) {
print "\nfound a protected file '$aname'\n";


Rather than a long list of file names with 'eq' and '&&' we can use a hash where the keys are the protected filenames. All we have to do is check to see if the filename exists as a key in the hash to know that it is special case.


Code
		foreach my $pair (@changelist) { 
my $was =$pair->[0];
my $is =$pair->[1];
# MAGIK SPOT --
my $ct = ($molestme=~s/$was/$is/g);
print "$was is now $is, changed $ct times\n" if ($ct);
}


The for loop is often better replaced with a foreach in Perl. In this case we do not need to keep $i around and can deal with the whole small array at once. $pair is a pointer the the inner array and we can use it to fill $was and $is. Remember that as $pair is a scalar that points to an array and not an actual array you will need to dereference its parts with the '->' notation.

I also added the $ct variable to capture how many times a substitution was made and the print statement will show that fact. Actually the print statement will only print if a subsitution was made to reduce clutter in the output stream.


Code
opendir(DIR, $sourcedir) || die "Can't open superuser path, $!";


When reading a file or directory and the whole filename is already in a variable it is much better to not wrap the single variable in double quotes. Adding the quotes forces perl to create a new string that is exactly the same as the variable that was already known. This does not apply to writing of course as the writing will need to add a '>' to the front of the filename.

Also you should always include $! in your die statements to learn a little more about the error. And you should not end your error message with a new line because you can get $! to tell you more if you do not end with "\n".


Code
	if (-d "$sourcedir/$aname") {


Remember that the output from readdir is the filename without any directory information. To use the filename when you are dealing with a directory other than where you currently are you usually have to use it as $dir/$filename.

One other thing to consider, especially as the number or size of your files increases, is that it might be better to read from $sourcedir and write your new files to $outputdir rather than read from $sourcedir, write an exact copy in $backupdir and then a modified copy in $sourcedir. It is probably much easier to move the directories around after the change.

Edited to fix a typo.


(This post was edited by rGeoffrey on Apr 29, 2002, 10:01 AM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives