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

I have a file with tokens in it. The tokens are enclosed by %%'s. The file has been read into a scalar ($Template). I find a line in $Template that contains a token I want to replace, actually I am going to replace one line in $Template with multiple lines.

I build the replacement, $ReplacementString, then I try to replace the original line $Original_Line with $ReplacementString, simple huh? Elegant Perl at it's best.

The problem seems to be that Perl cannot seem to find $Original_Line again. Is there some pointer I need to reset or something?

It seems like there is an undending ocean of regex stuff and I just have a dinghy to find what I need!

Doug
while($Template =~ /\n(.*%%$FindWhat.*%%.*\n)/i) { my $Original_Line = $1; my $ReplacementString = ''; my $First = true; # # Replace Each Column # foreach my $Column (@Columns) { my $New_Line = $Original_Line; foreach my $Field (keys(%$Column)) { $New_Line =~ s/%%$Field.*%%/$Column->{$Field}/i; } $ReplacementString = $ReplacementString . $New_Line; }
Here is an example Input File:
/* ------------------------------------------------------------------- +------------------------------------------------ ** %%TableName%%.Fl ** ------------------------------------------------------------------- +------------------------------------------------ ** FastLoad Script for %%TableName%% ** ------------------------------------------------------------------- +------------------------------------------------ ** Generated by LoadGen ** ------------------------------------------------------------------- +------------------------------------------------ ** What | Who | When | Why ** ------------------------------------------------------------------- +------------------------------------------------ ** 1.00 | %%Author_Initials%% | %%Date%% | Original Version ** ------------------------------------------------------------------- +------------------------------------------------ ** | | | ** ------------------------------------------------------------------- +------------------------------------------------ ** | | | ** ------------------------------------------------------------------- +------------------------------------------------ */ .Show Versions; .Sessions %%Sessions%%; .LogOn %%TPID%%/%%User%%,%%Password%%; Database %%StagingDB%%; Drop Table ET_%%TableName%%; Drop Table UV_%%TableName%%; Drop Table %%TableName%%_Stage; Create Set Table %%TableName%%_Stage , No FallBack , No Before Journal , No After Journal , CheckSum = High ( %%Column_Name_Comma%% Varchar(%%Column_Size%%) ) Primary Index ( %%Primary_Index_Column_Comma%% ) ; .Set Record %%Record_Type%%; Begin Loading %%TableName%%_Stage Errorfiles ET_%%TableName%%, UV_%%TableName%%; Help Table %%TableName%%_Stage; Define File = %%Data_Dir%%%%Data_FileName%%; Show; ErrLimit %%ErrLimit%%; Insert Into %%TableName%%_Stage Values ( :%%Column_Name_Comma%% ); End Loading; .LogOff;

Replies are listed 'Best First'.
Re: Problem Replacing Previously Found String
by ikegami (Patriarch) on Nov 17, 2004 at 05:31 UTC

    It's hard to help you because your template system doesn't seem to be well defined. Every time I look at it, I see another ambiguity. Most importantly, you seem to desire looping, yet there's nothing in the template telling the template system to loop. For example, I presume
        %%Column_Name_Comma%%    Varchar(%%Column_Size%%)
    should be repeated, seperated by commas, but your templating system has no means of knowing the line should be looped, and has no means of knowing a comma should be added between the lines should it be able to loop.

    It seems to me you should go back to the design stage. We can't really help until you know what you want. Given that, maybe you should be using an existing templating system instead of writing your own.

    By the way, it's usually easier to parse the template into a tree and work with the tree rather than the flat template. It makes looping trivial.

Re: Problem Replacing Previously Found String
by diotalevi (Canon) on Nov 16, 2004 at 23:17 UTC
    Use s///ge instead of while( //g ). If you want more you'll have to describe your input data. I had a response with code but had to erase it when I decided I couldn't guess at what the input looked like.
      Thanks for the reply. I need to avoid the "s///g" option because there may be multiple lines that I need to find and replace. Here is one of the input files:

      /* ------------------------------------------------------------------- +------------------------------------------------ ** %%TableName%%.Fl ** ------------------------------------------------------------------- +------------------------------------------------ ** FastLoad Script for %%TableName%% ** ------------------------------------------------------------------- +------------------------------------------------ ** Generated by LoadGen ** ------------------------------------------------------------------- +------------------------------------------------ ** What | Who | When | Why ** ------------------------------------------------------------------- +------------------------------------------------ ** 1.00 | %%Author_Initials%% | %%Date%% | Original Version ** ------------------------------------------------------------------- +------------------------------------------------ ** | | | ** ------------------------------------------------------------------- +------------------------------------------------ ** | | | ** ------------------------------------------------------------------- +------------------------------------------------ */ .Show Versions; .Sessions %%Sessions%%; .LogOn %%TPID%%/%%User%%,%%Password%%; Database %%StagingDB%%; Drop Table ET_%%TableName%%; Drop Table UV_%%TableName%%; Drop Table %%TableName%%_Stage; Create Set Table %%TableName%%_Stage , No FallBack , No Before Journal , No After Journal , CheckSum = High ( %%Column_Name_Comma%% Varchar(%%Column_Size%%) ) Primary Index ( %%Primary_Index_Column_Comma%% ) ; .Set Record %%Record_Type%%; Begin Loading %%TableName%%_Stage Errorfiles ET_%%TableName%%, UV_%%TableName%%; Help Table %%TableName%%_Stage; Define File = %%Data_Dir%%%%Data_FileName%%; Show; ErrLimit %%ErrLimit%%; Insert Into %%TableName%%_Stage Values ( :%%Column_Name_Comma%% ); End Loading; .LogOff;


      Doug

        So what kinds of things go into $FindWhat and how is that different than @Column's hashes? Why don't you try out Text::Template? It already does this stuff and without having to invent your own parser.

        If all else fails, you could track what $-[0] and $+[0] are when you copy $1 and later use substr() to write back to $Template at those offsets with your replacement.

        I don't buy your argument about not using s/// on the grounds that it appears you're rejecting it on some faulty assumptions. Regardless, here's a s///ge alike without actually using s///.

        # Ala my $region_start = $-[0]; my $region_length = $+[0] - $-[0]; my $OriginalLine = $1; ... substr $Template, $region_start, $region_length, $ReplacementString;
Re: Problem Replacing Previously Found String
by TedPride (Priest) on Nov 17, 2004 at 09:18 UTC
    Try something like the following:
    use strict; my %Column = ('TableName' => 'MyTable', 'Date' => '12-3-1979'); my $Template = join('', <DATA>); $Template =~ s/%%(.*?)%%/$Column{$1}/g; print $Template; __DATA__ ** FastLoad Script for %%TableName%% ** ------------------------------------------------------------------- +------------------------------------------------ ** Generated by LoadGen ** ------------------------------------------------------------------- +------------------------------------------------ ** What | Who | When | Why ** ------------------------------------------------------------------- +------------------------------------------------ ** 1.00 | %%Author_Initials%% | %%Date%% | Original Version ** -------------------------------------------------------------------
    Your problem was that your regex was greedy, taking the maximum length match rather than the shortest match. You can fix this by doing .*? instead of .* As you can see from the small section of test code above, the substitutions should work fine for you now.
      Thanks, that helped enormously! Doug