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

I've got a little program that opens a file to read then writes it to a NEW file. I'll like to search out some keywords and replace them in the NEW file.

I am having trouble figuring out how to do a substitute string and then continue to write out the rest of the content with NEW edit in the NEW file.

#!usr/bin/perl # program takes a base part and changes some fields to show new part # use strict; #use warning; #use 5.280; use Data::Dumper; use Fcntl qw(:flock SEEK_END); use Cwd 'abs_path'; # Info from Base Part Job report my $base_part_test = '5237all5x_a1_rpt.txt'; my $base_part_file = '5237all5x'; my $base_part_file_uc = uc $base_part_file; my $base_part_rev = 'a1'; my $base_part_rev_lc = lc $base_part_rev; my $base_part_Customer = 'SA'; my $base_path = '/Product/SA_customer_product/'.$base_part_Customer.'/ +'.$base_part_file_uc.'/'.$base_part_file_uc.'_rev_'.$base_part_rev_lc +.'/RetBuildReport'; my $Real_base_part_file = $base_path.'/'.$base_part_file.'_'.$base_par +t_rev.'_rpt.txt'; #check if the Report file exists in the directory then do the substitu +tion of builder. if ( -f $Real_base_part_file) { # Info to New Buld Report my $New_part_report = 'NEW_PART_REPORT.txt'; # Test what's inside the vars print Dumper ( $base_part_file, $base_part_rev , $base_path, $Real_bas +e_part_file ); # Creating a file system "touch $New_part_report"; my $New_Part_Date = system "date"; open my $base_fh , '+<', $base_part_test or die; open my $New_fh ,'>', $New_part_report or die; while(my $line = <$base_fh>) { if ($line eq "Builder : Xi Wong"){ print " found original builder : "; Want to substitute builder Xi Wong to Lucca P. but not + sure how??? # then continue to write the rest of report print $New_fh $line; } } close($base_fh); close($New_fh); }

These are the substitution I am trying to change on the fly. If you can give me an idea of doing one substitution then I can do the rest of them. I just need some help to go about it

Original base part to start. This File gets read. In his case, Builder is Xi Wong but can be others

--------------------------------------- Basic Information: --------------------------------------- Build Report Rev: 02 + Builder: Xi Wong Part Name: MG5237ALL5X + Customer Name: SI Route: S5H2-12A

This shows only the builder substitution from old to new. In this case, it is Lucca P. but can be others

--------------------------------------- Basic Information: --------------------------------------- Build Report Rev: 01 + Builder: Lucca P. Part Name: MG5415DP + Customer Name: SA Route: S5H2-12A

Replies are listed 'Best First'.
Re: Edit a New file in place after reading it in
by GrandFather (Saint) on Dec 21, 2021 at 23:28 UTC

    First uncomment the use warning and change 'warning' to 'warnings'. Your intent to use strictures (strict and warnings) is good, but ignoring errors is really bad.

    Now consider the following self contained example script:

    use strict; use warnings; my $template = <<FILE; --------------------------------------- Basic Information: --------------------------------------- Build Report Rev: 02 Builder: Xi Wong Part Name: MG5237ALL5X Customer Name: SI Route: S5H2-12A FILE my $outText; my %subs = ( 'Build Report Rev' => '01', 'Builder' => 'Lucca P.', 'Part Name' => 'MG5415DP', 'Customer Name' => 'SA', 'Route' => 'S5H2-12A' ); # Info to New Buld Report open my $base_fh, '<', \$template; open my $New_fh, '>', \$outText; while (my $line = <$base_fh>) { if ($line =~ /^([^:]+):(\s+)/ && exists $subs{$1}) { $line = "$1:$2$subs{$1}\n"; } print $New_fh $line; } close $base_fh; close $New_fh; print $outText;

    Prints:

    --------------------------------------- Basic Information: --------------------------------------- Build Report Rev: 01 Builder: Lucca P. Part Name: MG5415DP Customer Name: SA Route: S5H2-12A

    which I think does what you want. Notice the hash used to store the substitution values. In a real life example that could be populated from a database or from a file containing the required edits. Although, in real life it feels like this whole exercise should be done using a database. You may be interested in Databases made easy for an introduction to using databases.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

      Grandfather, I appreciate the help on going about substituting the old values for the new values. I didn't figure out I could use a hash. I was having a blockage. Your code captured the task I wanted to get done. I now have to do this for 100's of records. I will also read your tutorial on databases (SQLlite) before attempting to clean up my code and show the updates here. I may have more questions coming when using the SQL database to write up my code. Thanks, Perlynewby

        I am having a bit of doubt about deciphering the regex; so, here's my best shot. Please correct me when I am wrong

         $line =~ /^([^:]+):(\s+)/ && exists $subs{$1}

        So, regex on $line is as follows (according to me ;-) )

        "^" regex string must be at the start of the line.

        The string is grouped by using the "( )", if the entire string in the "()" matches then it will substitute with a new value in the hash.

        I am not sure why the class  "[ ]" are opened or mean in this regex...???

        Whatever is in the class bracket, it is looking for a match of"^:" character. I am not sure about the carrot "^" does here before the ":" ?? then the class bracket closes.

        The "+" plus signs indicates that there may be more strings that match the ":" character ...maybe? I am guessing here. then we close the group.

        Then the character ":" appears again after the parenthesis has closed. I'm not sure what this ":" does here.

        then (s+) there may be one or more spaces after the first regex match.

        Then checks if that regex it just performed exists, if it does, then the new hash value replaces old to new value.

        I could not run your code because it kept giving me an error

        Error

        reading file No such file or directory

        Any ideas why this "reading file does not exist" when it was integrated in the code

    Re: Edit a New file in place after reading it in
    by GotToBTru (Prior) on Dec 21, 2021 at 23:20 UTC
      Help us help you with a Short, Self-Contained, Correct Example

      Lines 44 - 63 of your sample code would have been sufficient, if I am understanding your question correctly.

      my %substitutions = ( 'Xi Wong' => 'Lucca P.', 'MG5237ALL5X' => 'MG54 +15DP', 'SI' => 'SA' ); while (my $line = <$base_fh>) { foreach my $was (keys %substitutions) { $line =~ s/$was/$substitutions{$was}/g; } print $New_fh $line; }
      But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

    Re: Edit a New file in place after reading it in
    by BillKSmith (Monsignor) on Dec 21, 2021 at 22:39 UTC
      You need a regular expression (perlreref) for the 'PATTERN' in a substitution operator (s/PATTERN/REPLACEMENT/msixpodualngcer). Your single example does not provide enough information for us to offer much help in writing that regex.
      Bill
    Re: Edit a New file in place after reading it in
    by jkeenan1 (Deacon) on Dec 22, 2021 at 14:44 UTC
      In addition to the guidance provided by other commenters above, you should note that the data sample you provided has much trailing whitespace. You may have accidentally entered that whitespace when creating your original post -- but it also be present in the real-world data you are analyzing.

      After chomping a given line of input, you should trim off the trailing whitespace before applying any regex pattern to the line. That way, Xi Wong        $ will be reduced to Xi Wong$ and will be better detected during pattern matching.

      Jim Keenan
    Re: Edit a New file in place after reading it in
    by soonix (Chancellor) on Dec 23, 2021 at 09:32 UTC
      #use warning; #use 5.280;
      GrandFather already commented on "use warnings".

      The other line has a similiar problem:

      use 5.280 is short for version 5.280.0, while the current stable version is 5.34.0, so I'd assume you intended use 5.028;
        Another way would be to say use v5.28;
    Re: Edit a New file in place after reading it in
    by Anonymous Monk on Dec 22, 2021 at 14:07 UTC

      Not relevant to your original question, but:

      Perl's open will create a file if it does not exist. There would normally be no need to shell out to create it before you open it.

      my $New_Part_Date = system "date"; assigns $New_Part_Date the exit status of the date command (probably 0). The actual date goes to the terminal that runs the Perl script. In general if you want to capture the output of a command you use something like my $New_Part_Date = `date`;. But in this specific case I would do my $New_Part_Date = localtime;, which does not involve shelling out.

        Yes, I was expecting Perl to create the file but it wasn't. I Googled for the answer for this but I couldn't make it work so I added that system "touch" to create it for me. I'd like to know why Perl was having a hard time creating the file on my Linux shell; it is still a mystery to me. Yes, I will use your suggestion to have the file time stamped. Grazie mille!

          Your code checks for errors from open (good) but it just calls die on failure (not so good). Better would be to include what you were trying to open as well as the contents of the $! variable which will tell you what the problem was (showing just creating your output handle but similar message are useful for opening files for input as well):

          open my $outfh, '>', $New_part_report or die "Problem opening '$New_part_report' for writing: $!\n";

          The cake is a lie.
          The cake is a lie.
          The cake is a lie.

    Re: Edit a New file in place after reading it in
    by Anonymous Monk on Dec 22, 2021 at 14:36 UTC

        Oh, sorry. I didn't realize you were reading from one file and writing to another. Never mind.