gotsilk has asked for the wisdom of the Perl Monks concerning the following question:

hey monks, so im still fairly new to perl and have a simple problem i cant seem to figure out. So basically this script will read a specific file, look for a string using reg ex, and swap the match with another string. i can get it to compile but doesn't seem to modify the file at all.

open IN,"<filename.txt" or die "Could not open file : $! \n"; my $step_num = 0; while (<IN>) { if (/-----------step [\d]\./){ $step_num ++; s/-----------step \d\./-----------step $_\.$step_num/; } } close IN;

the result im looking for is this.... from -----------step 1.

to this -----------step 1.1

next occurence in file -----------step 1.2

next occurence in file -----------step 1.3

Replies are listed 'Best First'.
Re: Modifying a file using RegEx
by AppleFritter (Vicar) on Mar 23, 2015 at 20:08 UTC

    Your code isn't modifying the file because it's not writing to it; the substitution only affects the string in memory. That it has been read from a file is irrelevant at that time.

    The easiest way to accomplish what you want is to use perl's -i switch; see perlrun for more.

Re: Modifying a file using RegEx
by GotToBTru (Prior) on Mar 23, 2015 at 20:17 UTC

    You are reading from the file, changing a variable in the program, but don't write the changes back out. Typically, we open one file, make changes in memory, and then write out to a second file. At this point, we could overwrite the old with the new, or preserve both. See open.

    Follow the link in the link! That is where I meant to go in the first place.

    Dum Spiro Spero
Re: Modifying a file using RegEx
by Laurent_R (Canon) on Mar 23, 2015 at 23:26 UTC
    This a possible approach (untested code):
    use strict; use warnings; my $infile = "filename.txt"; my $outfile = "filename.out"; open my $IN, "<", $infile or die "Could not open file $infile : $!"; open my $OUT, ">", $outfile or die "Could not open file $outfile : $!" +; my $step_num = 0; while (<$IN>) { if (/-----------step [\d]\./){ $step_num ++; s/-----------step \d\./-----------step $_\.$step_num/; } print $_ $OUT; } close $IN; close $OUT;
    Once you are happy with the content of the filename.out file, you can add code to delete or rename in input file and rename your output file to the original source file, if needed (or to rename the input file before hand and write directly to an output file having the name of the input file, if you prefer).

    Although there are some work-around solutions, in general you can't read from and write to the same free-format file, and this is essentially true in almost any programming language.

    The two main solutions are the following:

    1. open the input file, load it all into memory (e.g. in an array of lines), close the file, modify its content in memory, open the same file in write mode (which will clutter its original content), and print the result into it (this is basically what's going on behind the scenes when you open a text file with your favorite editor or with a word processor);
    2. Read the file and write to another file (as suggested in my code above), and do the necessary house cleaning (renaming the files) afterward or possibly before (this is what the -i command-line option suggested by AppleFritter if essentially doing behind the scene).

    There are a few "more advanced" solutions, such as using tieed variables (i.e. doing even more complicated things behind the scene), but I would suggest that you stay with either of the two simple solutions described above for the time being.

    As an additional point, please note that I made a few other changes to your code, adding the use strict; use warnings; pragmas (you should always have them, except possibly for one-liners), and changing the syntax for opening a file to the "3-argument" syntax, this is not absolutely necessary, but this is considered to be good practice nowadays. This "new" syntax was introduced, I believe, with Perl 5.6, so that's more than ten years ago.

    Je suis Charlie.
      ... introduced ... with Perl 5.6 ... more than ten years ago.

      Indeed, perlhist sez the release date for 5.6.0 was "2000-Mar-22", so more (by one day as I post this in my TZ) than fifteen years ago!

      What were you doing then?


      Give a man a fish:  <%-(-(-(-<

        Time flies like an arrow. From pure memory, I thought 5.6 was relased 12 or 13 years ago, I had written "more than 10" to be on the safe side of things. Thanks ++, I now will remember I can write "more than 15 years ago" new time I want to speak about that.

        Je suis Charlie.
Re: Modifying a file using RegEx
by jeffa (Bishop) on Mar 23, 2015 at 23:25 UTC

    I'm not sure exactly what your data looks like. How many lines are then to be replaced? Do the step numbers change and if they do, how? So i came up with a candidate data file that looks like this:

    -----------step 1.
    -----------step 1.
    -----------step 1.
    -----------step 2.
    -----------step 1.
    -----------step 1.
    -----------step 1.
    -----------step 2.
    -----------step 2.
    -----------step 2.
    -----------step 3.
    -----------step 3.
    -----------step 2.
    -----------step 2.
    -----------step 3.
    -----------step 3.
    

    The following one liner will transform the data into hopefully something that is very close to want you want.

    perl -pi -e's/(-----------step\s+)(\d+)\./$h{$2}||=$2;$h{$2}+=.1;$1.$h +{$2}/ge' foo.txt; cat foo.txt -----------step 1.1 -----------step 1.2 -----------step 1.3 -----------step 2.1 -----------step 1.4 -----------step 1.5 -----------step 1.6 -----------step 2.2 -----------step 2.3 -----------step 2.4 -----------step 3.1 -----------step 3.2 -----------step 2.5 -----------step 2.6 -----------step 3.3 -----------step 3.4
    If not, then please feel free to post more examples of the data you are working with.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)