📄 ch01_12.htm
字号:
<HTML><HEAD><TITLE>Recipe 1.11. Indenting Here Documents (Perl Cookbook)</TITLE><METANAME="DC.title"CONTENT="Perl Cookbook"><METANAME="DC.creator"CONTENT="Tom Christiansen & Nathan Torkington"><METANAME="DC.publisher"CONTENT="O'Reilly & Associates, Inc."><METANAME="DC.date"CONTENT="1999-07-02T01:29:11Z"><METANAME="DC.type"CONTENT="Text.Monograph"><METANAME="DC.format"CONTENT="text/html"SCHEME="MIME"><METANAME="DC.source"CONTENT="1-56592-243-3"SCHEME="ISBN"><METANAME="DC.language"CONTENT="en-US"><METANAME="generator"CONTENT="Jade 1.1/O'Reilly DocBook 3.0 to HTML 4.0"><LINKREV="made"HREF="mailto:online-books@oreilly.com"TITLE="Online Books Comments"><LINKREL="up"HREF="ch01_01.htm"TITLE="1. Strings"><LINKREL="prev"HREF="ch01_11.htm"TITLE="1.10. Interpolating Functions and Expressions Within Strings"><LINKREL="next"HREF="ch01_13.htm"TITLE="1.12. Reformatting Paragraphs"></HEAD><BODYBGCOLOR="#FFFFFF"><img alt="Book Home" border="0" src="gifs/smbanner.gif" usemap="#banner-map" /><map name="banner-map"><area shape="rect" coords="1,-2,616,66" href="index.htm" alt="Perl Cookbook"><area shape="rect" coords="629,-11,726,25" href="jobjects/fsearch.htm" alt="Search this book" /></map><div class="navbar"><p><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch01_11.htm"TITLE="1.10. Interpolating Functions and Expressions Within Strings"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 1.10. Interpolating Functions and Expressions Within Strings"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><B><FONTFACE="ARIEL,HELVETICA,HELV,SANSERIF"SIZE="-1"><ACLASS="chapter"REL="up"HREF="ch01_01.htm"TITLE="1. Strings"></A></FONT></B></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch01_13.htm"TITLE="1.12. Reformatting Paragraphs"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 1.12. Reformatting Paragraphs"BORDER="0"></A></TD></TR></TABLE></DIV><DIVCLASS="sect1"><H2CLASS="sect1"><ACLASS="title"NAME="ch01-32093">1.11. Indenting Here Documents</A></H2><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch01-pgfId-1153">Problem <ACLASS="indexterm"NAME="ch01-idx-1000010286-0"></A><ACLASS="indexterm"NAME="ch01-idx-1000010286-1"></A><ACLASS="indexterm"NAME="ch01-idx-1000010286-2"></A></A></H3><PCLASS="para">When using the multiline quoting mechanism called a <EMCLASS="emphasis">here document</EM>, the text must be flush against the margin, which looks out of place in the code. You would like to indent the here document text in the code, but not have the indentation appear in the final string value.</P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch01-pgfId-1159">Solution</A></H3><PCLASS="para">Use a <CODECLASS="literal">s///</CODE><ACLASS="indexterm"NAME="ch01-idx-1000010292-0"></A> operator to strip out leading whitespace.</P><PRECLASS="programlisting"># all in one($var = <<HERE_TARGET) =~ s/^\s+//gm; your text goes hereHERE_TARGET# or with two steps$var = <<HERE_TARGET; your text goes hereHERE_TARGET$var =~ s/^\s+//gm;</PRE></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch01-pgfId-1189">Discussion</A></H3><PCLASS="para">The substitution is straightforward. It removes leading whitespace from the text of the here document. The <CODECLASS="literal">/m</CODE> modifier lets the <CODECLASS="literal">^</CODE> character match at the start of each line in the string, and the <CODECLASS="literal">/g</CODE> modifier makes the pattern matching engine repeat the substitution as often as it can (i.e., for every line in the here document).</P><PRECLASS="programlisting">($definition = <<'FINIS') =~ s/^\s+//gm; The five varieties of camelids are the familiar camel, his friends the llama and the alpaca, and the rather less well-known guanaco and vicu馻.FINIS</PRE><PCLASS="para">Be warned: all the patterns in this recipe use <CODECLASS="literal">\s</CODE><ACLASS="indexterm"NAME="ch01-idx-1000010293-0"></A>, which will also match newlines. This means they will remove any blank lines in your here document. If you don't want this, replace <CODECLASS="literal">\s</CODE> with <CODECLASS="literal">[^\S\n]</CODE> in the patterns.</P><PCLASS="para">The substitution makes use of the property that the result of an assignment can be used as the left-hand side of <CODECLASS="literal">=~</CODE>. This lets us do it all in one line, but it only works when you're assigning to a variable. When you're using the here document directly, it would be considered a constant value and you wouldn't be able to modify it. In fact, you can't change a here document's value <EMCLASS="emphasis">unless</EM> you first put it into a variable.</P><PCLASS="para">Not to worry, though, because there's an easy way around this, particularly if you're going to do this a lot in the program. Just write a subroutine to do it:</P><PRECLASS="programlisting">sub fix { my $string = shift; $string =~ s/^\s+//gm; return $string;}print fix(<<"END"); My stuff goes hereEND# With function predeclaration, you can omit the parens:print fix <<"END"; My stuff goes hereEND</PRE><PCLASS="para">As with all here documents, you have to place this here document's target (the token that marks its end, <CODECLASS="literal">END</CODE> in this case) flush against the left-hand margin. If you want to have the target indented also, you'll have to put the same amount of whitespace in the quoted string as you use to indent the token.</P><PRECLASS="programlisting">($quote = <<' FINIS') =~ s/^\s+//gm; ...we will have peace, when you and all your works have perished--and the works of your dark master to whom you would deliver us. You are a liar, Saruman, and a corrupter of men's hearts. --Theoden in /usr/src/perl/taint.c FINIS$quote =~ s/\s+--/\n--/; #move attribution to line of its own</PRE><PCLASS="para">If you're doing this to strings that contain code you're building up for an <CODECLASS="literal">eval</CODE>, or just text to print out, you might not want to blindly strip off all leading whitespace because that would destroy your indentation. Although <CODECLASS="literal">eval</CODE> wouldn't care, your reader might.</P><PCLASS="para">Another embellishment is to use a special leading string for code that stands out. For example, here we'll prepend each line with <CODECLASS="literal">@@@</CODE>, properly indented:</P><PRECLASS="programlisting">if ($REMEMBER_THE_MAIN) { $perl_main_C = dequote<<' MAIN_INTERPRETER_LOOP'; @@@ int @@@ runops() { @@@ SAVEI32(runlevel); @@@ runlevel++; @@@ while ( op = (*op->op_ppaddr)() ) ; @@@ TAINT_NOT; @@@ return 0; @@@ } MAIN_INTERPRETER_LOOP # add more code here if you want}</PRE><PCLASS="para">Destroying indentation also gets you in trouble with poets.</P><PRECLASS="programlisting">sub dequote;$poem = dequote<<EVER_ON_AND_ON; Now far ahead the Road has gone, And I must follow, if I can, Pursuing it with eager feet, Until it joins some larger way Where many paths and errands meet. And whither then? I cannot say. --Bilbo in /usr/src/perl/pp_ctl.cEVER_ON_AND_ONprint "Here's your poem:\n\n$poem\n";</PRE><PCLASS="para">Here is its sample output:</P><PRECLASS="programlisting"><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>Here's your poem: </I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>Now far ahead the Road has gone,</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I> And I must follow, if I can,</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>Pursuing it with eager feet,</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I> Until it joins some larger way</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>Where many paths and errands meet.</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I> And whither then? I cannot say.</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I> --Bilbo in /usr/src/perl/pp_ctl.c</I></CODE></B></CODE></PRE><PCLASS="para">The following <CODECLASS="literal">dequote</CODE><ACLASS="indexterm"NAME="ch01-idx-1000010296-0"></A> function handles all these cases. It expects to be called with a here document as its argument. It checks whether each line begins with a common substring, and if so, strips that off. Otherwise, it takes the amount of leading whitespace found on the first line and removes that much off each subsequent line.</P><PRECLASS="programlisting">sub dequote { local $_ = shift; my ($white, $leader); # common whitespace and common leading string if (/^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/) { ($white, $leader) = ($2, quotemeta($1)); } else { ($white, $leader) = (/^(\s+)/, ''); } s/^\s*?$leader(?:$white)?//gm; return $_;}</PRE><PCLASS="para">If that pattern makes your eyes glaze over, you could always break it up and add comments by adding <CODECLASS="literal">/x</CODE>:</P><PRECLASS="programlisting"> if (m{ ^ # start of line \s * # 0 or more whitespace chars (?: # begin first non-remembered grouping ( # begin save buffer $1 [^\w\s] # one byte neither space nor word + # 1 or more of such ) # end save buffer $1 ( \s* ) # put 0 or more white in buffer $2 .* \n # match through the end of first line ) # end of first grouping (?: # begin second non-remembered grouping \s * # 0 or more whitespace chars \1 # whatever string is destined for $1 \2 ? # what'll be in $2, but optionally .* \n # match through the end of the line ) + # now repeat that group idea 1 or more $ # until the end of the line }x ) { ($white, $leader) = ($2, quotemeta($1)); } else { ($white, $leader) = (/^(\s+)/, ''); } s{ ^ # start of each line (due to /m) \s * # any amount of leading whitespace ? # but minimally matched $leader # our quoted, saved per-line leader (?: # begin unremembered grouping $white # the same amount ) ? # optionalize in case EOL after leader }{}xgm;<ACLASS="indexterm"NAME="ch01-idx-1000010297-0"></A></PRE><PCLASS="para">There, isn't that much easier to read? Well, maybe not; sometimes it doesn't help to pepper your code with insipid comments that mirror the code. This may be one of those cases.</P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch01-pgfId-1407">See Also</A></H3><PCLASS="para">The "Scalar Value Constructors" section of <EMCLASS="emphasis">perldata</EM> (1) and the <ACLASS="olink"HREF="../prog/ch02_03.htm#PERL2-CH-2-SECT-3.2.6">"Here Documents"</A> section of <ACLASS="olink"HREF="../prog/ch02_01.htm">Chapter 2</A> of <ACLASS="citetitle"HREF="../prog/index.htm"TITLE="Programming Perl"><CITECLASS="citetitle">Programming Perl</CITE></A>; the <CODECLASS="literal">s///</CODE> operator in <EMCLASS="emphasis">perlre </EM>(1) and <ICLASS="filename">perlop</I> (1), and the <ACLASS="olink"HREF="../prog/ch02_04.htm">"Pattern Matching"</A> section of <ACLASS="olink"HREF="../prog/ch02_01.htm">Chapter 2</A> of <ACLASS="citetitle"HREF="../prog/index.htm"TITLE="Programming Perl"><CITECLASS="citetitle">Programming Perl</CITE></A><ACLASS="indexterm"NAME="ch01-idx-1000010288-0"></A><ACLASS="indexterm"NAME="ch01-idx-1000010288-1"></A></P></DIV></DIV><DIVCLASS="htmlnav"><P></P><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch01_11.htm"TITLE="1.10. Interpolating Functions and Expressions Within Strings"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 1.10. Interpolating Functions and Expressions Within Strings"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="book"HREF="index.htm"TITLE="Perl Cookbook"><IMGSRC="../gifs/txthome.gif"ALT="Perl Cookbook"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch01_13.htm"TITLE="1.12. Reformatting Paragraphs"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 1.12. Reformatting Paragraphs"BORDER="0"></A></TD></TR><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228">1.10. Interpolating Functions and Expressions Within Strings</TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="index"HREF="index/index.htm"TITLE="Book Index"><IMGSRC="../gifs/index.gif"ALT="Book Index"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228">1.12. Reformatting Paragraphs</TD></TR></TABLE><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><FONTSIZE="-1"></DIV<!-- LIBRARY NAV BAR --> <img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p> <a href="copyrght.htm">Copyright © 2002</a> O'Reilly & Associates. All rights reserved.</font> </p> <map name="library-map"> <area shape="rect" coords="1,0,85,94" href="../index.htm"><area shape="rect" coords="86,1,178,103" href="../lwp/index.htm"><area shape="rect" coords="180,0,265,103" href="../lperl/index.htm"><area shape="rect" coords="267,0,353,105" href="../perlnut/index.htm"><area shape="rect" coords="354,1,446,115" href="../prog/index.htm"><area shape="rect" coords="448,0,526,132" href="../tk/index.htm"><area shape="rect" coords="528,1,615,119" href="../cookbook/index.htm"><area shape="rect" coords="617,0,690,135" href="../pxml/index.htm"></map> </BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -