Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Writing to a File 2

Status
Not open for further replies.

jriggs420

Programmer
Sep 30, 2005
116
US
Hello again, I know that it's possible to create a file, and then write too it fairly easily in perl. But what about writing to a file that already exists, I have I notion that this must be possible. I'd like to take it a step further; Is it possible to write to a specified spot in a file. If so, is the write location determined by linenum or by regexs, or (hopefully) either/both.

I know that you can overwrite any/all text w/ perl by using the '>file' (or '>>file' to append) open method, but what about just overwrtting certain specified lines? Any generic code or links would be must appreciated, TIA,

Average Joe user.

A clever person solves a problem.
A wise person avoids it.

-- Einstein
 
Code:
open( FH, ">>$file" )
appends to $file. If you want to write to a specific place in a file, you have [tt]seek()[/tt] as in C and other languages. see perlfaq5 for more details.

When you say "overwriting certain specified lines" (my italics), I worry that you're trying to use a text file as a database. There are much better ways to do this: look at Tie::RDBM and others. While we see a file in "lines", there is no OS support for this view - it's an ordered collection of bytes. This makes line-by-line access inefficient.

Yours,

fish

["]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.["]
--Maur
 
No, I'm not using a text file as a database, I know better than that. Thanks for the link though 'seek' is the word I was looking for. So is it possible to overwrite specified line(s), or no?

A clever person solves a problem.
A wise person avoids it.

-- Einstein
 
Yes but it is not obvious since the line you wish to overwrite with needs to be the same length as the one that was there before.
Should you wish to write a longer or shorter line you need to buffer the entire rest of the file and append it after the updated line.
Not efficient!


Trojan.
 
Lazy approach:
Code:
my @a = <FH>;
$a[$index] = $newval . "\n";
seek( FH, 0, 0 );
print FH join( '', @a );

1st optimisation:
Code:
my @a = <FH>;
$a[$index] = $newval . "\n";
my $offset = 0;
$offset += length($_) foreach (@a[0..$index-1]);
seek( FH, $offset, 0 );
print FH join( '', @a[$index..$#a] );
(untested)

2nd optimisation with fixed record lengths:
Code:
seek( FH, $index * $reclen, 0 );
my $current = sprintf( '%'.$reclen.'s', $newval ); # dangerous
while(1) {
   last unless read( FH, my $next, $reclen ); # does this work?
   seek( FH, $index++ * $reclen, 0 ); 
   print FH $current;
   $current = $next;
}
(untested)

For small files you could get away with this but it would be worth standing back a bit: the first example that occurred to me was a configuration file but there are better tools for that. I'm struggling for a second example.

I'm more than happy to back down if I'm wrong but I'd really like you to post more of your spec and would bet a pint (North Hampshire, UK) that there's a module that does it without you having to address files by line.

Yours,

fish

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
There is a module Tie::File but my work computer (the one on which I am doing the programming) doesn't have that particular module, and the chances of admin letting me install it are nil. Back to the drawing board......

A clever person solves a problem.
A wise person avoids it.

-- Einstein
 
hehe - you can install a module without admin priviledges. Stick it somewhere where you have write permission and add that directory to @INC with -I on the command line, -I on the shebang line or a push in a BEGIN block. Tie::File is pure perl so it should work a treat.

If you have a look at the source of Tie::File you'll see quite how non-trivial it is and I hope that this should dissuade you from re-inventing this particular wheel.

Yours,

f

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
or you can code in the script the path to the module

Code:
# Set path to user modules
use lib qw(full path to module folder);

# Use  Module
use ModuleName;

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Yes but then the script will run only on his machine.

Corwin
 
obviously it needs to be changed relative to the machine it is running on!, it was just another example of how you can use modules not installed.

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Wow!That module is a nice piece of coding. I don't think I'm going to install anything behind admin's back. (They're gonna find out sooner or later they could be reading this Email for all I know), so I've opted for a different soluion.

Tell me if you think this solution is viable: Set up an INFILE loop w/ while until a pre-determined variable (prob. some random text string) is matched, then print those lines I was talking about above, then print INFILE to eof. Here's the code I have so far (untested):
Code:
open (INFILE , "input") || die;
open (OUTFILE, ">output") || die;
       
        while (<INFILE>){
   if (!/rndstr/){
        print OUTFILE;}
   else{print OUTFILE $newdata;}
                         }
close (INFILE);
close (OUTFILE);
The files in question shouldn't be anymore than 300 lines. Comments anyone? Please disregard any typos, if possible.
p.s. I have the best boss in the world!

A clever person solves a problem.
A wise person avoids it.

-- Einstein
 
That would work. The only change I'd make is to stop checking each line after you've found the good one:
Code:
open (INFILE , "input") || die;
open (OUTFILE, ">output") || die;
       
while (<INFILE>){
   if (!/rndstr/) {
     print OUTFILE;
   } else {  
     print OUTFILE $newdata, [red]"\n"[/red];
     while (<INFILE>) { # trivial copy for remainder
       print OUTFILE;
     }
   }
}
close (INFILE);
close (OUTFILE);
or you may prefer
Code:
open (INFILE , "input") || die;
open (OUTFILE, ">output") || die;
       
while (<INFILE>){
   if (!/rndstr/) {
     print OUTFILE;
   } else {  
     print OUTFILE $newdata, [red]"\n"[/red];
     last;
   }
}

while (<INFILE>) { # trivial copy for remainder
  print OUTFILE;
}

close (INFILE);
close (OUTFILE);

Note that you need to ensure that $newdata is already delimited or add a delimiter yourself (as above) when printing.

Yours,

f

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
Why do we need the second while and exit the first?
What does "trivial copy for reminder" mean?

Corwin
 
PS the best boss in the world would certainly understand the huge productivity gains that come from tapping the enormous wealth of perl modules instead of reinventing every wheel. If he hasn't effectively communicated his enlightenment to you then I'm sure he'd welcome the opportunity to use Tie::File's 2614 lines as an example to show you the advantages of letting other people do the work and make (and then fix) the mistakes for you. I'm sure that, if he's reading this, he's on his way down the corridor to your office as I type...

Yours,

fish

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
Thanks again fish. Quick follow-up, if you're still around. Looking at the second example you've given, it looks like the 2nd 'while' operation is going simply re-copy the entire input file after the data written by the 1st 'while' opereation. Am I wrong? If so, why?

A clever person solves a problem.
A wise person avoids it.

-- Einstein
 
The second loop doesn't copy the entire file because the handle is still open from the first loop, so it copies from where it left off without testing each line against the regex. For example, if your file has 300 lines and you are updating the second, it will do the regex check on the first two lines and then, because it knows that it has found what it wants, it whizzes through the rest (2nd loop) without looking, saving 298 redundant regex operations.

It doesn't change the effect of running the code but it should improve performance (and just feels right).

f

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
Theorhettically (sp?)would it be possible to remove (not print) some lines that I don't want in the data? Sorry to seem like I total dunce, but I am stuck again..I have 'inserted' the data (input) lines no problem. Maybe a sample setup will demonstrate.

___INPUT_____
These are some cool people:
-Bill Gates
-Dick Cheney

print rest of INPUT afterwards...

_Desired_OUTPUT__
These are some cool people:
-Linus Torvalds
-Yoda

remove Bill/Dick replace w/Linus/Yoda print rest of INPUT to OUTPUT...

So I can't do a s/Bill Gates/Linus Torvalds/ because the way my program is setup, these names are actually variable( generated in a separate script), and can be 1-45 string-long list of names. jeez I think I've just confused myself even more. Again, not a database honest.




A clever person solves a problem.
A wise person avoids it.

-- Einstein
 
Do you want them on the same places, where Bill Gates and Dick Cheney are, or they could be at the end of the file?

To remove some lines you just check if the names are you don't want to print are there with regexpr or substr.
Let's say:

Code:
next if (/Bill Gates/ || /Bill Cheney/);
print OUTPUT;

Corwin
 
I've just read through this again and, if you're not going to have more than 300 lines, why not just grab the lot into an array, munge the array as you wish and then spurt it all back out again?

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top