File edits in a Perlish way

P

pgodfrin

I have a need to pass a series of "edits" I'd like to apply in a
somewhat controlled manner.

My pseudo code would be something like:
1. accept as input the file name, old text, new text
2. check existence in file for old text, if not found error out
3. if found, then show before and after text for the whole line
4. Write out file.

I'm currently using @ARGV for #1 and I intend to slurp the whole file
into an array and then use map for #4.

I'm looking for ideas on a "perlish" way to do #2 and #3. The grep
function lets me show before and after like so:

print grep(/$oldtx/,@oldfile);

But I haven't checked for existence of the $oldtx as of yet. so a type
would make this print statement print nothing. I could always use
loops and other brute force methods - but it seems like there may be a
cooler, perlish way to this... Any ideas?
phil
 
C

cartercc

1. accept as input the file name, old text, new text

print "Enter file name:";
my $filename = <STDIN>;
chomp $filename;
print "Enter old text:";
my $oldtext = <STDIN>;
chomp $oldtext;
print "Enter new text:";
2. check existence in file for old text, if not found error out

open INFILE, "<$filename" or die "INFILE ERROR, $!";
open OUTFILE, ">outfile.txt" or die "OUTFILE ERROR, $!";
3. if found, then show before and after text for the whole line
4. Write out file.

while(<INFILE>)
{
print OUTFILE if /$oldtext/;
# maybe this as well ...
# next unless /$oldtext/;
# $_ = s/$oldtext/$newtext/;
# print;
}
close INFILE;
close OUTFILE;

CC
 
J

Jürgen Exner

pgodfrin said:
I have a need to pass a series of "edits" I'd like to apply in a
somewhat controlled manner.

Are those edits limited to one line at a time(a) or can a single edit
spread across multiple lines(b) ?
My pseudo code would be something like:
1. accept as input the file name, old text, new text
2. check existence in file for old text, if not found error out
3. if found, then show before and after text for the whole line
4. Write out file.

I'm currently using @ARGV for #1
Ok

and I intend to slurp the whole file into an array

If (a) then ususally it is better to process the file line by line
instead of slurping it in all at once.
If (b) then manipulating the file in memory is probably easier.
and then use map for #4.

Most people would use print() to write something.
I'm looking for ideas on a "perlish" way to do #2

perldoc -f index

if (index($line, $oldtext) > -1) {
print $line;
apply_edit($line, $oldtext, $newtext);
print $line
}
The grep
function lets me show before and after like so:

print grep(/$oldtx/,@oldfile);

But I haven't checked for existence of the $oldtx as of yet. so a type
would make this print statement print nothing.

I have no idea what you are trying to do with that code snippet. It
doesn't look like anything related to "show before and after text"

jue
 
R

Ron Bergin

I have a need to pass a series of "edits" I'd like to apply in a
somewhat controlled manner.

My pseudo code would be something like:
1. accept as input the file name, old text, new text
2. check existence in file for old text, if not found error out
3. if found, then show before and after text for the whole line
4. Write out file.

I'm currently using @ARGV for #1 and I intend to slurp the whole file
into an array and then use map for #4.

I'm looking for ideas on a "perlish" way to do #2 and #3. The grep
function lets me show before and after like so:

print grep(/$oldtx/,@oldfile);

But I haven't checked for existence of the $oldtx as of yet. so a type
would make this print statement print nothing. I could always use
loops and other brute force methods - but it seems like there may be a
cooler, perlish way to this... Any ideas?
phil

Do you want the output to go to a new file, or simply edit/update the
source file?

If outputting to a new file, how do you want to determine the
filename?

If you're simply updating 1 or more lines in the source file, you may
want to look at using Tie::File

Could the search string be found more than once in the file? If so,
do you want to change all occurrences or just the first?

For step #1 instead of using @ARGV, I'd either prompt the user for
input or use one of the Getopt modules.
http://search.cpan.org/~jv/Getopt-Long-2.37/lib/Getopt/Long.pm
http://search.cpan.org/~rgarcia/perl-5.10.0/lib/Getopt/Std.pm
http://search.cpan.org/~rsavage/Getopt-Simple-1.49/lib/Getopt/Simple.pm

Step #2 is handled with an "or die" statement when creating the
filehandle.

Steps #3 & #4 would be best handled while looping through the source
file line-by-line.
 
P

pgodfrin

Do you want the output to go to a new file, or simply edit/update the
source file?

If outputting to a new file, how do you want to determine the
filename?

If you're simply updating 1 or more lines in the source file, you may
want to look at using Tie::File

Could the search string be found more than once in the file?  If so,
do you want to change all occurrences or just the first?

For step #1 instead of using @ARGV, I'd either prompt the user for
input or use one of the Getopt modules.http://search.cpan.org/~jv/Getopt-L...avage/Getopt-Simple-1.49/lib/Getopt/Simple.pm

Step #2 is handled with an "or die" statement when creating the
filehandle.

Steps #3 & #4 would be best handled while looping through the source
file line-by-line.

Since these files are relatively small - 30 to 60 lines, I'm ok with
manipulating them in memory. They won't get any larger because the
files are command files that are generated by other unrelated
processes. As such, I could always process them line by line and have
the ultimate control, but I was just looking for some neat features
similar to the map function.

What I want to do is check for the "old text" IN the file not if the
file exists.

Showing before and after snippet is:

print "This will change from:\n";
print grep(/$oldtx/,@oldfile);
print "...to\n";
print grep(s/$newtx/t2ns1/g,@oldfile);

But if the first print grep statement does not find any of the $oldtx
in @oldfile, if the user made a typo for example, then it just prints
nothing. That why I would like to test the existence of the $oldtext.
I suppose:

unless (grep(/$oldtx/,@oldfile)) { die "not found!\n";}

would work. And, since these are very small files, then it would be ok
to do grep multiple times...

pg
 
R

Ron Bergin

On Sep 29, 10:08 am, pgodfrin <[email protected]> wrote:
[snip]

Since these files are relatively small - 30 to 60 lines, I'm ok with
manipulating them in memory. They won't get any larger because the
files are command files that are generated by other unrelated
processes. As such, I could always process them line by line and have
the ultimate control, but I was just looking for some neat features
similar to the map function.

What I want to do is check for the "old text" IN the file not if the
file exists.

Showing before and after snippet is:

print "This will change from:\n";
print grep(/$oldtx/,@oldfile);
print "...to\n";
print grep(s/$newtx/t2ns1/g,@oldfile);

But if the first print grep statement does not find any of the $oldtx
in @oldfile, if the user made a typo for example, then it just prints
nothing. That why I would like to test the existence of the $oldtext.
I suppose:

unless (grep(/$oldtx/,@oldfile)) { die "not found!\n";}

would work. And, since these are very small files, then it would be ok
to do grep multiple times...

pg

What's wrong with something like this (untested):

my $found;

open my $filehadle, '<', $file or die "can't open '$file' $!";
while ( <$filehandle> ) {
my $line = $_;

if ( $line =~ s/$oldtx/$newtx/ ) {
$found++;
print "Original:$_";
print "New:$line";
}
}
die "Search pattern was not found\n" unless $found;
 
R

Ron Bergin

[snip]
Showing before and after snippet is:

print "This will change from:\n";
print grep(/$oldtx/,@oldfile);
print "...to\n";
print grep(s/$newtx/t2ns1/g,@oldfile);

But if the first print grep statement does not find any of the $oldtx
in @oldfile, if the user made a typo for example, then it just prints
nothing. That why I would like to test the existence of the $oldtext.
I suppose:

unless (grep(/$oldtx/,@oldfile)) { die "not found!\n";}

would work. And, since these are very small files, then it would be ok
to do grep multiple times...

pg

Why would you want to intensionally add in inefficiency of the
multiple greps? Granted the files are small and the loss of
efficiency may not be noticed in this case, but to me intensionally
adding inefficiency and possible loss of clarity doesn't make sense.
The loop I showed in my prior comment is clean, efficient and easy to
follow.
 
P

pgodfrin

[snip]




Showing before and after snippet is:
print "This will change from:\n";
print grep(/$oldtx/,@oldfile);
print "...to\n";
print grep(s/$newtx/t2ns1/g,@oldfile);
But if the first print grep statement does not find any of the $oldtx
in @oldfile, if the user made a typo for example, then it just prints
nothing. That why I would like to test the existence of the $oldtext.
I suppose:
 unless (grep(/$oldtx/,@oldfile)) { die "not found!\n";}
would work. And, since these are very small files, then it would be ok
to do grep multiple times...

Why would you want to intensionally add in inefficiency of the
multiple greps?  Granted the files are small and the loss of
efficiency may not be noticed in this case, but to me intensionally
adding inefficiency and possible loss of clarity doesn't make sense.
The loop I showed in my prior comment is clean, efficient and easy to
follow.

Hi Ron,
Yup your code is good, clean (and fun). I'm just looking for a way to
make the code very, very simple. So, in this case, multiple grep
statements is very, very simple. Albeit not as efficient, but very
simple. In fact I was hoping for something even simpler - where I
could combine the grep and an error check into one line. sort of like:
if(print grep(/$oldtx/,@oldfile)) {print "Text not found\n" and
die;}

But that won't work case printing the result of grep is always
successful.

pg
 
B

Ben Morrow

Quoth pgodfrin said:
I have a need to pass a series of "edits" I'd like to apply in a
somewhat controlled manner.

My pseudo code would be something like:
1. accept as input the file name, old text, new text
2. check existence in file for old text, if not found error out
3. if found, then show before and after text for the whole line
4. Write out file.

I'm currently using @ARGV for #1 and I intend to slurp the whole file
into an array and then use map for #4.

I'm looking for ideas on a "perlish" way to do #2 and #3. The grep
function lets me show before and after like so:

print grep(/$oldtx/,@oldfile);

my @lines = grep /\Q$oldtx/, @oldfile
or die "'$oldtx' not found\n";
print @lines;
 
R

Ron Bergin

Hi Ron,
Yup your code is good, clean (and fun). I'm just looking for a way to
make the code very, very simple. So, in this case, multiple grep
statements is very, very simple. Albeit not as efficient, but very
simple. In fact I was hoping for something even simpler - where I
could combine the grep and an error check into one line. sort of like:
if(print grep(/$oldtx/,@oldfile)) {print "Text not found\n" and
die;}

But that won't work case printing the result of grep is always
successful.
IMO, you seem to have an odd idea of the meaning of simple.

It sounds to me that what you're really after is the most compact code
and not necessarily the simplest.

while ( <$filehandle> ) {
my $line = $_;
# this if statement is 1 line, but probably wrap in the email
if($line =~ s/$oldtx/$newtx/){$found++; print "Original:$_", "New:
$line";}
}
 
R

Ron Bergin

Hi Ron,
Yup your code is good, clean (and fun). I'm just looking for a way to
make the code very, very simple. So, in this case, multiple grep
statements is very, very simple. Albeit not as efficient, but very
simple. In fact I was hoping for something even simpler - where I
could combine the grep and an error check into one line. sort of like:
if(print grep(/$oldtx/,@oldfile)) {print "Text not found\n" and
die;}

But that won't work case printing the result of grep is always
successful.

pg

Here's a clean, efficient, and very simple approach doesn't require
multiple greps.

#!/usr/bin/perl

use strict;
use warnings;
use Tie::File;

# checks should be done here, but I kept it simple
die "invalid args" unless @ARGV == 3;

my ($file, $oldtxt, $newtxt) = @ARGV;
my $found;

tie my @array, 'Tie::File', $file or die "can't tie '$file' $!";

for ( @array ) {
my $original = $_;
if ( s/\Q$oldtxt/$newtxt/ ) {
$found++;
print "Updating:$original\n", "To:$_\n";
}
}
untie @array;
die "Search pattern was not found\n" unless $found;
 
R

Ron Bergin

} >I have a need to pass a series of "edits" I'd like to apply in a
} >somewhat controlled manner.
}
} Are those edits limited to one line at a time(a) or can a single edit
} spread across multiple lines(b) ?
} >and I intend to slurp the whole file into an array
}
} If (a) then ususally it is better to process the file line by line
} instead of slurping it in all at once.
} If (b) then manipulating the file in memory is probably easier.

Processing a file in-place [as he said he wants to do,

He never specifically stated that he wanted to do an-inplace edit,
although it is a reasonable interpretation.

4. Write out file.

That could also be interpreted as writing out to a new file.
since his
pseudo-code didn't include an output file] is pretty tricky to do line by
line, no?
No, it's not that tricky.
What part do you see as being tricky.

There are several approaches, 1 being the use of Tie::File as I showed
in a prior post.

Another option would be to use Perl's $^I ( $INPLACE_EDIT ) and loop
through the file line-by-line.

A third option would be to load the file into an array and then reopen
the file in write mode and loop through & process the array outputting
it to the file.

I'm sure I can come up with other viable options, but I think those
are the most common approaches.
 
P

pgodfrin

Quoth pgodfrin <[email protected]>:


I have a need to pass a series of "edits" I'd like to apply in a
somewhat controlled manner.
My pseudo code would be something like:
1. accept as input the file name, old text, new text
2. check existence in file for old text, if not found error out
3. if found, then show before and after text for the whole line
4. Write out file.
I'm currently using @ARGV for #1 and I intend to slurp the whole file
into an array and then use map for #4.
I'm looking for ideas on a "perlish" way to do #2 and #3. The grep
function lets me show before and after like so:
    print grep(/$oldtx/,@oldfile);

    my @lines = grep /\Q$oldtx/, @oldfile
        or die "'$oldtx' not found\n";
    print @lines;

--
'Deserve [death]? I daresay he did. Many live that deserve death. And some die
that deserve life. Can you give it to them? Then do not be too eager to deal
out death in judgement. For even the very wise cannot see all ends.'
                                                               (e-mail address removed)

Ben,
Nice to hear from you again. And, once again, you da man...

I think Ron, that you are right, I 'm looing for compact code.
Nevertheless, simplicity is in the eye of the beholder. In my OP I
mentioned that I could do this using "loops and other brute force
methods" - the implication that loops are a brute force and therefore
not elegant. I apologize for that implication. In general I am not
opposed to using loops - I just didn't want to do that this time. But
to be fair, the grep and map functions already use loops and return
count values - which does kinda the same thing your code does below.

smiles,
pg

"Frodo Lives!"
 
T

Tad J McClellan

Bernie Cosell said:
Processing a file in-place [as he said he wants to do, since his
pseudo-code didn't include an output file] is pretty tricky to do line by
line, no?


No.

eg: Add line numbers to a file in-place:

perl -p -i -e 's/^/$. /' some.file

not at all tricky.


What _is_ trickier is applying multi-line edits in-place (which
is why the question was asked).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top