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

My question is, How can I insert text after the match with the multiline text has been performed. I don't want to replace any text. All I have to do is to insert text after a match with multi line text has been made
use strict; use warning; open IN, 'result.txt' or die "Error opening result.txt: $!"; local $/ = undef; $_ = <IN>; close IN; # this is the multi line text to be searched for my $pattern=" the patten is in many line and @"; # after the match, there is some multi line text to be inserted, which + I don't know how to. if(/\Q$pattern/){ splice @array, $_ +1, 0, "inserting pattern in many lines with spaces "; } open OUT, '>', 'result.txt' or die "Error opening result.txt: $!"; print OUT $_; close OUT;

Replies are listed 'Best First'.
Re: multi line text insert
by Zaxo (Archbishop) on Sep 29, 2003 at 03:45 UTC

    There are two basic ways to insert text into a string. The substr function is preferred if you know the offset where the new text is to go,

    substr $_, $offset, 0, $added_text; # or substr( $_, $offset, 0) = <<EOT; Some text to add. EOT

    The other way is with the substitution operator, s///. That is preferred when the insertion position is determined by context, i.e. by regex matching. To match across line ends, you may want to use the /s flag so that '.' is allowed to match "\n", and it sounds as if you want to do multiple matches, so the /g flag is also called for. s/$pattern/$new_text/sg; More detail about your pattern and the substitute text would allow better advice.

    After Compline,
    Zaxo

Re: multi line text insert
by dws (Chancellor) on Sep 29, 2003 at 03:51 UTC
    How can I insert text after the match with the multiline text has been performed?

    The technique is actually quite simple. Basically, you're looking to do something like   $string =~ s/($pattern)/$1$stuffToAdd/g; where $pattern is something that will match across multiple lines. If you want metacharacters to match newlines, you'll need to use the /s modifier.

    All this is explained in perlre, which is part of the standard on-line help that accompanies Perl. If you're using ActiveState, it's in the on-line Perl documentation that you can find via   Start | Programs | ActiveState ActivePerl | Documentation If you're on *nix, type perldoc perlre at your favorite shell prompt.

Re: multi line text insert
by davido (Cardinal) on Sep 29, 2003 at 03:46 UTC
    If you use capturing paranthesis in conjunction with a backreference you will be able to keep what matched while still adding to it. In a simple single-line example, this works:
    my $text = "Here is some text."; my $insert = "boring "; $text =~ s/(some\s)/$1$insert/;

    Captures can be a little confusing. Just understand that what matches within parenthesis is captured into $1, $2, etc. You're going to use one set of parens, so when you get a match, $1 contains what matched. That means that s/(pattern)/$1/; is substituting what matched with what matched (ie, not doing much of anything). But when you add the $insert portion after (which can be any scalar, or literal text) you now are matching, keeping what matched, and adding something to it.

    But you want to match a string that contains newlines in it. For that, you either need to specify those newlines within the regexp, or use the /s modifier at the end of the match operator so that the s///s operator will treat multiline strings as all one entity (and so that dot and \s match on newlines too).

    my $text = "Here is some\nmulti-lined text"; my $insert = "thrilling " $text =~ s/(some\s)/$1$insert/s;

    If you want to continue the same substitution multiple times, also use the /g modifier, like this:

    $text =~ s/(some\s)/$1$insert/sg;

    If your pattern itself is in multiple lines, that's still ok, it just means that there are newlines embedded in the pattern itself. Still use the /sg modifier, and things will be ok.

    my $pattern = "Text with\nnewlines in it"; my $text = ......lots of stuff..... $text =~ s/(\Q$pattern\E)/$1$insert/sg;

    You might find some useful tips in perlretut.

    Now in your example you were trying to make use of substr. That's fine. As Zaxo pointed out, substr works quite efficiently if you already know the position at which you wish to insert text. If you use a pattern match to find that position, you use the pos function to determine the position of the match, so that you know where to insert. But if you've gone to the trouble of matching with a regexp, you may as well follow through with a simple s///sg operator rather than a "m//sg", plus a "pos", plus a "substr", all nested within a "while" loop!

    Hope this helps!

    Dave

    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein

Re: multi line text insert
by sgifford (Prior) on Sep 29, 2003 at 04:39 UTC
    If you use the /g modifier, you can use the pos function to find out where the match ended:
    #!/usr/bin/perl -w use strict; my $str = "pencil pen marker highlighter pencil pen "; my $insert = "crayon\n"; while ($str =~ /pencil\npen\n/mg) { my $pos = pos($str); substr($str,$pos,0) = $insert; pos($str) = $pos+length($insert); } print $str;