in reply to Not A Rockstar File Manipulator Today

The only problem I had with your code is the 'S' symbol. Under the 'strict' pragma the program requires 's' for the substitution expression.
Now I can't match oko1 in code size (nor anywhere else), but inspired from the funny discovery that your arguments are in alphabetic order, here is my code
use strict; my @params=qw(input output pattern replacement); my %settings; @settings{@params}=("0")x @params; foreach my $key (sort keys %settings) { print "$key="; chomp($settings{$key}=<>); } open IN,"<",$settings{"input"} || die "not a rockstar today"; -e $settings{"output"} and die("File Exists..will not override!") or o +pen(OUT,">",$settings{"output"}) or die "Error while opening file: $! +"; while (<IN>) { # s/$settings{"pattern"}/$settings{"replacement"}/g; print OUT $_; } # end while close (IN); close (OUT);

Replies are listed 'Best First'.
Re^2: Not A Rockstar File Manipulator Today
by TGI (Parson) on Nov 15, 2008 at 20:08 UTC

    The good:

    • 3 argument open. Very much safer than the two arg flavor.
    • automating the parameter collection

    The bad:

    • input parsing is overly complicated.
    • Big chains of and/or logic are hard to maintian.
    • <> has extra magic. I'd just use STDIN. Unless you want the magic.
    • No quotes needed in hash keys.
    • High precedence logical or (||) in the line where you open the input file doesn't do what you think it does.
    use strict; use warnings; my %settings; foreach my $key (qw(input output pattern replacement)) { print "$key = "; my $setting = <STDIN>; chomp $setting; $settings{$key}=$setting; } open IN, '<', $settings{input} or die "Can't open input file $settings{input}: $!"; die "Output file $settings{output} already exists\n" if -e $settings{output}; open OUT, '>', $settings{output} or die "Can't open output file $settings{output}: $!"; while (<IN>) { s/$settings{pattern}/$settings{replacement}/g; print OUT $_; } close (IN); close (OUT);

    Now, there are still some problems with this code. The big one is that using a hash and keys for storing the settings, looses us the benefits of using strictures in the first place. If I misspell 'output' when I use it, I'll get no warnings. Perl happily autovivifies a value in the hash and returns an undef.

    There are ways around this issue. The easiest is to use Hash::Util to lock the hash, so that attempts to change the hash or access nonexistent keys become a fatal error.

    use Hash::Util qw(lock_hash); my %settings; foreach my $key (qw(input output pattern replacement)) { print "$key = "; my $setting = <STDIN>; chomp $setting; $settings{$key}=$setting; } lock_hash %settings;

    Another thing is the use of global file handles. I should really be using lexical filehandles. Global handles are global variables, with all the attendant problems.


    TGI says moo

      Wow! I see your points TGI, thanks! About the '||' and 'or' thing this was my bad, I was aware of the difference. I think using and's and or's is pretty cool. Coming from a Java/C# background I'd say they are most expressive features. I have not been into big Perl projects yet, so I guess I have not faced the drawbacks.
      Anyway, I cleaned up more code using the map{} magic and current 'version' is:
      use strict; use warnings; my @params=qw(input output pattern replacement); my %settings; @settings{@params}=("0")x @params; foreach my $key (sort keys %settings) { print "$key="; chomp($settings{$key}=<>); } open IN,"<",$settings{input} or die "not a rockstar today"; -e $settings{output} and die("File Exists..will not override!") or open(OUT,">",$settings{output}) or die "Error while opening file: $!"; print OUT map { if(s/$settings{pattern}/$settings{replacement}/g) {$_} else {$_} } (<IN>); close (IN) and close (OUT);

      As a freshman, I just can't enough of this kind of writing! :)

        The second take is much more readable. But you are doing some extra, unnecessary work when you initialize your settings hash. What does setting each key to '0' do for you? It's easier to let perl create the keys in the loop as it goes. Also, why sort the keys of the settings hash. Sort may be a simple looking command, but it can get expensive to sort big arrays. Just put your params array in the order you want to use. So you wind up iterating over your param array twice, and sorting it once.

        Any setting value will always be defined, as at least the empty string, so there is no need to initialize your hash values.

        foreach my $key (@params) { print "$key = "; my $value = <>; # must at minimum be "\n" chomp $value; # must at minimum be "" redo unless length $value; # rerun loop if value is empty string +. }

        Also the or between checking for the output file and the opening it is unnecessary. If you die from the open failing, you die, and execution stops. The following is identical in behavior to yours:

        -e $settings{output} and die "File Exists..will not override!"; open(OUT,">",$settings{output}) or die "Error while opening file: $!";

        It's amazing how you can do more with less in perl.

        I think "The Right Thing To Do"tm as we end the script is to check the results of the handle closes.

        close IN or warn "Error closing input $!\n"; close OUT or warn "Error closing output $!\n";

        Perl is a lot of fun to play with. When I first started I went crazy for the ternary operator ($foo = $bar > 3 ? 'BIG' : 'small';) and used for all sorts of things. Keep playing and experimenting. You will learn from it. Keep your old code and come back and look at it months later. You'd be amazed at what you learn from this, too.

        If you can manage it on a student budget pick up PBP. It is an excellent book. It has tons of advice from grizzled veterans who've stepped in many of the traps that you are just discovering. For fun, and if you want to blow your mind, take a look at Higher Order Perl. Dominus does a great job introducing functional programming techniques in perl.

        Also, I can't recommend wandering around the The Portland Pattern Repository enough.

        Mainly, keep having fun and learning new stuff.


        TGI says moo