File edits in a Perlish way

Discussion in 'Perl Misc' started by pgodfrin, Sep 29, 2008.

  1. pgodfrin

    pgodfrin Guest

    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
     
    pgodfrin, Sep 29, 2008
    #1
    1. Advertising

  2. pgodfrin

    cartercc Guest

    On Sep 29, 1:08 pm, pgodfrin <> wrote:
    > 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:";
    my $newtext = <STDIN>;
    chomp $newtext;

    > 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
     
    cartercc, Sep 29, 2008
    #2
    1. Advertising

  3. pgodfrin <> wrote:
    >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

    >and #3.


    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
     
    Jürgen Exner, Sep 29, 2008
    #3
  4. pgodfrin

    Ron Bergin Guest

    On Sep 29, 10:08 am, pgodfrin <> wrote:
    > 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.
     
    Ron Bergin, Sep 29, 2008
    #4
  5. pgodfrin

    pgodfrin Guest

    On Sep 29, 1:12 pm, Ron Bergin <> wrote:
    > On Sep 29, 10:08 am, pgodfrin <> wrote:
    >
    >
    >
    > > 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-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
     
    pgodfrin, Sep 29, 2008
    #5
  6. pgodfrin

    Ron Bergin Guest

    On Sep 29, 11:37 am, pgodfrin <> wrote:
    > On Sep 29, 1:12 pm, Ron Bergin <> wrote:
    >
    >
    >
    > > On Sep 29, 10:08 am, pgodfrin <> 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;
     
    Ron Bergin, Sep 29, 2008
    #6
  7. pgodfrin

    Ron Bergin Guest

    On Sep 29, 11:37 am, pgodfrin <> wrote:

    [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.
     
    Ron Bergin, Sep 29, 2008
    #7
  8. pgodfrin

    pgodfrin Guest

    On Sep 29, 2:37 pm, Ron Bergin <> wrote:
    > On Sep 29, 11:37 am, pgodfrin <> wrote:
    >
    > [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.


    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
     
    pgodfrin, Sep 29, 2008
    #8
  9. pgodfrin

    Ben Morrow Guest

    Quoth 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);


    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.'
     
    Ben Morrow, Sep 29, 2008
    #9
  10. pgodfrin

    Ron Bergin Guest

    On Sep 29, 12:54 pm, pgodfrin <> wrote:
    >
    > 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";}
    }
     
    Ron Bergin, Sep 29, 2008
    #10
  11. pgodfrin

    Ron Bergin Guest

    On Sep 29, 12:54 pm, pgodfrin <> wrote:
    [snip]
    >
    > 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;
     
    Ron Bergin, Sep 29, 2008
    #11
  12. pgodfrin

    Ron Bergin Guest

    On Sep 30, 5:18 am, Bernie Cosell <> wrote:
    > Jürgen Exner <> wrote:
    > } pgodfrin <> wrote:
    > } >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.
     
    Ron Bergin, Sep 30, 2008
    #12
  13. pgodfrin

    pgodfrin Guest

    On Sep 29, 3:04 pm, Ben Morrow <> wrote:
    > Quoth 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);

    >
    >     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.'
    >                                                                


    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!"
     
    pgodfrin, Sep 30, 2008
    #13
  14. Bernie Cosell <> wrote:

    > 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).


    --
    Tad McClellan
    email: perl -le "print scalar reverse qq/moc.noitatibaher\100cmdat/"
     
    Tad J McClellan, Sep 30, 2008
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. gabriele renzi

    disappearing of global perlish variables

    gabriele renzi, Feb 20, 2004, in forum: Python
    Replies:
    3
    Views:
    270
    gabriele renzi
    Feb 20, 2004
  2. Chris

    Perlish dictionary behavior

    Chris, Jun 3, 2004, in forum: Python
    Replies:
    2
    Views:
    284
    Larry Bates
    Jun 3, 2004
  3. Fred Allen

    Perlish dictionary behavior

    Fred Allen, Jun 9, 2004, in forum: Python
    Replies:
    1
    Views:
    269
    Chris Liechti
    Jun 9, 2004
  4. Bob Walton
    Replies:
    9
    Views:
    190
    David K. Wall
    Sep 26, 2003
  5. hofer
    Replies:
    8
    Views:
    106
Loading...

Share This Page