http://www.perlmonks.org?node_id=948849

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

I'm trying to send some e-mails in perl. Basically, the e-mails are going to consist of two generic chunks of text (strings) with some custom defined stuff in the middle. I'm trying to layout the first and second generic chunks of text as $msg_bdy_1 and $msg_bdy_2, and then print those strings into the e-mail object later on. So I'm trying to do something along these lines:

my $msg_body_1 = 'line 1\nline 2\nline3a\t\line3b\nline4\n\n'; my $msg_body_2 = '\n\nline 5\nline 6\nline7a\t\line8b\nline8\n\n'; my $other_crap = 'blah blah blah'; print $msg_body_1 $other_crap $msg_body_2;

However, when I write the code up, I don't want $msg_body_1 and $msg_body_2 to be one long line of unreadable crap. I'd like to be able to lay it out something along the lines of:

my $msg_body_1 = ' line 1\n line 2\n line3a \t \line3b\n line4 \n\n '; my $msg_body_2 = ' \n\n line 5\n line 6\n line7a \t \line7b\n line8 \n\n '; my $other_crap = 'blah blah blah'; print $msg_body_1 $other_crap $msg_body_2;

The problem is, when I actually write out the strings like the second example in my script, extra newline characters get added for every character break that I include (I don't only get character breaks on \n, I get character breaks on \n and every time I add an actual carriage return to the string with the "Enter" button).

I'd very much like to be able to lay out long, complex strings like this in an indented manner so that my code is readable later. But I don't know how to make the code both readable, and define the string with newlines and tabs only where I explicitly make calls to \n and \t.

I thought I could do somethings like:

qq/ some string stuff in here/x;

But apparently the /x modifer only works with m// and not the quote operators.

Am I missing something? Is this even possible in perl?

Thanks,
Brady C. Jackson

Replies are listed 'Best First'.
Re: Formatting Strings Without Interpolating Whitespace
by kejohm (Hermit) on Jan 19, 2012 at 21:56 UTC

    You could use a here-doc, like this:

    my $msg_body_2 = <<"EOT"; line 5 line 6 line7a\tline7b line8 EOT

    or you could split the string in to segments spread over multiple lines and concatenate them together:

    my $msg_body_2 = "\n\n" . "line 5\n" . "line 6\n" . "line 7a\tline7b\n" . "line 8\n" . "\n\n";

      The concatenation option is the simplest and easiest for me to work in now. I'll try to fiddle with some of the other options later, but for now this works perfect. Thanks for the tip kejohm. I didn't realize you could space concatenation operators across multiple lines.

      Cheers,
      Brady C. Jackson

Re: Formatting Strings Without Interpolating Whitespace
by chromatic (Archbishop) on Jan 19, 2012 at 21:54 UTC

    This answer seems so facile that I'm certain I must have missed something, but what if you remove \n from within your strings?

    (In this case I prefer to use a heredoc, but a multi-line quoted string also works.)


    Improve your skills with Modern Perl: the free book.

      That was actually my first solution, but when I start increasing the complexity of the formatting from what I've shown here, that solution falls a bit short. Still, not a bad way to brute force it if I don't want to risk exploding my code all over the place by testing Xiong's solution.

Re: Formatting Strings Without Interpolating Whitespace
by Xiong (Hermit) on Jan 19, 2012 at 21:51 UTC

    Yes. Not complex.

    # lib/Anti/Tidy.pm package Anti::Tidy; use overload # Overload Perl operations '""' => \&_stringify, ; sub new { return bless {}, shift }; sub _stringify { my $self = shift; for ( $self->{-string} ) { s/[\t\n]//gs; s/\\t/\t/g; s/\\n/\n/g; }; return $self; }; sub put_string { my $self = shift; $self->{-string} = shift; return $self; }; # bin/yourscript.pl use Anti::Tidy; my $uglyfruit = Anti::Tidy->new; $uglyfruit->put_string('Hideous \t big old string with embedded hard tabs and hard newlines to be ignored\n but with escape \t sequences to be \n expanded.'); say $uglyfruit;

    This code is totally untested and may or may not work, or may explode, scattering wheels and cogs across your bench. You may want to fool with the regexes to handle spaces on either side of your tabs and newlines of either persuation.

    But this should do the trick. I assume no responsibility for the result.

    I'm not the guy you kill, I'm the guy you buy. —Michael Clayton
Re: Formatting Strings Without Interpolating Whitespace
by TomDLux (Vicar) on Jan 20, 2012 at 03:13 UTC

    Why do you want to specify newlines explicitly as \n? If you are generating a significant document, why are the strings even part of the code file? Separate the text from the code and you can send out different languages; your designers can rearrange the message with insignificant change to the program.

    Put message snippets into separate text files, if you need various options, laid out exactly as you want it to look. If you want to interpolate data in the middle of standard text, use one of the templating modules, some of them work for text, not just html.

    As Occam said: Entia non sunt multiplicanda praeter necessitatem.

      That would involve expanding this particular script's capabilities far beyond what is needed right now. Down the line I do intend to do exactly as you say (one problem of course, is that if I use external template files then I have to version control the template files if they start getting updated). However, I needed to get something out the door today, and digging through/researching templating modules while simultaneously trying to get my formats right, my parsing right, and my e-mail configuration right was going to require a lot of time that I don't have right now.

      One day, if all goes well, I fully intend to evolve this into a completely modular, badass notification program. But for the time being, that amount of work would not be adding value to my immediate task at hand

      So, long story short, I'm prioritizing.

Re: Formatting Strings Without Interpolating Whitespace
by JavaFan (Canon) on Jan 19, 2012 at 22:20 UTC
    I don't only get character breaks on \n, I get character breaks on \n and every time I add an actual carriage return to the string with the "Enter" button
    Well, yes, of course. (Although, since you use single quotes in your code snippets, \n aren't interpolated -- but I'll consider that a copy-and-paste error). What do you expect Perl to do? Ignore your \ns? Ignore your newlines? Why should it? If you have newlines in your strings (be it because of \n, or a "real" newline) that you don't want, don't put them there!
      No, I expected perl to behave exactly as I said it is. My question is asking if there is a way to modify that behavior.
        Just do exactly what I said, don't include the newlines you don't want:
        my $msg_body_1 = "line 1\n" . "line 2\n" . "line3a \t \line3b\n" . "line4" . "\n\n" ;
Re: Formatting Strings Without Interpolating Whitespace
by CountZero (Bishop) on Jan 20, 2012 at 07:13 UTC
    Why not use one of the many templating modules? I like Template Toolkit at lot, but it may be overkill for a simple application. On the other hand, "simple" applications tend to grow over time and then you are happy to have a full fledged templating engine on board.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Thanks for the tip and link Count. I tend to use my old SOPW write-ups as design notes for my programs as I evolve them. And now I have a handy trail to start following once I get around to implementing templating.

      Cheers!