<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"  href="/xslt/final.xslt"?><html>
  <head>
    <title>XSLT- примеры. Вып.4 Строковые операции (reverse, duplicate, replace, uppercase)</title>
    <meta name="css" content=""/>
    <meta name="js" content=""/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="id" content="25"/>
    <link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml"/>
  </head>
  <body>
    <div class="main">
      <div class="wrap">
        <div class="L">
          <h1><a href="/">..</a> / XSLT- примеры. Вып.4 Строковые операции (reverse, duplicate, replace, uppercase)</h1>
          <ol class="tags big">
            <li>
              <a href="/xslt-recursive">xslt-recursive</a>
            </li>
            <li>
              <a href="/xslt-examples">xslt-examples</a>
            </li>
          </ol>
          <div class="myContent"><p>Одна из проблем, с которой сталкиваются при изучении XSLT1.0, является скудость библиотеки встроенных функций, в том числе функций работы со строками. Кажется что в любом языке программирования должна иметь место функция замены строк, но увы, в XSLT1.0 ее нет. Нет и других относительно часто встречающихся функций. Предполагается, что подобные операции должны быть проведены на уровне подготовке данных.</p>
<p>В версии XSLT2 разработчики существенно расширили арсенал строковых (и не только) функций, но судя по тому сколько пришлось ждать полноценой поддержки XSLT2 в PHP, надо забыть об этом на несколько лет. Впрочем Microsoft тоже не спешит. </p>
<p>Сейчас же нам приходится выкручиваться, изобретая собственные функции. Итак начнем.</p>
<h2>Пример I. XSLT-uppercase. Перевод букв в верхний регистр</h2>
<p>Вообще-то использовать такую функцию, применительно к  HTML  глупо, и даже вредно - для этого есть <a href="#" title="www.htmlbook.ru/css/text-transform.html" rel="nofollow" class="external">соответствующее CSS-свойство</a>  Но в качестве учебного примера можно поиграть в собственную функцию XSLT-uppercase .</p>
<p>Те кто  немного знает Perl или PHP должны были с ней сталкиваться с функцией:</p>
<p><em>translate (arg1,arg2,arg3)</em></p>
<p>Функция <em>translate() </em>преобразует  строку <em>arg1</em>, заменяя в ней буквы встречающиеся в  строке <em>arg2</em>, в соответствии с их позицией заменены на символы из строки <em>arg3</em>. На основе этой функции и строится перевод букв в верхний регистр.</p>

<table class="code">
    <tbody>
        <tr>
            <td><!--php-->
            <pre class="brush: plain">&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
      
       &lt;xsl:template match="/"&gt;            
           &lt;xsl:call-template name="uppercase"&gt;            
               &lt;xsl:with-param name="input"  select="'Превед Медвед!'" /&gt;            
           &lt;/xsl:call-template&gt;            
       &lt;/xsl:template&gt;            
      
       &lt;xsl:template name="uppercase"&gt;            
           &lt;xsl:param name="input"/&gt;            
           &lt;xsl:value-of select="translate($input,
           'йцукенгшщзхъфывапролджэячсмитьбю',   
           'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ')"/&gt;            
       &lt;/xsl:template&gt;            
      
   &lt;/xsl:stylesheet&gt;            
</pre><!--/php--></td>
        </tr>
    </tbody>
</table>
<p>Аналогично будет работать и перевод в нижний регистр <em>lowercase()</em>.</p>
<h2>Пример II. XSLT-duplicate. Повтор строки</h2>
<p>Этот пример использует <a href="/tags/xslt-recursive/">рекурсивный вызов</a> - один из основных в XSLT алгоритмов   построения циклов. Мы уже рассматривали его в примере <a href="/article/23">построения алфавитных указателей</a></p>

<table class="code">
    <tbody>
        <tr>
            <td><!--php-->
            <pre class="brush: plain">   &lt;xsl:template match="*"&gt;

       &lt;xsl:call-template name="duplicate"&gt;            
           &lt;xsl:with-param name="count"&gt;              3&lt;/xsl:with-param&gt;            
           &lt;xsl:with-param name="input"&gt;              Ура!&lt;/xsl:with-param&gt;            
       &lt;/xsl:call-template&gt;            

   &lt;/xsl:template&gt;            

   &lt;xsl:template name="duplicate"&gt;            
       &lt;xsl:param name="count" select="1"/&gt;            
       &lt;xsl:param name="input" select="1"/&gt;            

       &lt;xsl:choose&gt;            
           &lt;!--  выход из рекурсии  --&gt;            
           &lt;xsl:when test="$count &lt;1"/&gt;            
           &lt;xsl:when test="not($input)"/&gt;            
           &lt;xsl:otherwise&gt;            

               &lt;xsl:value-of select="$input"/&gt;            
               &lt;!--  рекусивный вызов  --&gt;            
               &lt;xsl:call-template name="duplicate"&gt;            
                   &lt;xsl:with-param name="count" select="$count - 1"/&gt;            
                   &lt;xsl:with-param name="input" select="$input"/&gt;            
               &lt;/xsl:call-template&gt;            

           &lt;/xsl:otherwise&gt;            

       &lt;/xsl:choose&gt;            

   &lt;/xsl:template&gt;            
</pre><!--/php--></td>
        </tr>
    </tbody>
</table>
<p>Процедура репликации - именованный шаблон (<em>duplicate</em>) на входе содержит два параметра <em>count </em>- количество повторов и входную строку<em> input</em>.  Внутри именованного шаблона происходит уменьшение <em>count</em> на единицу и рекурсивное обращение функции самой к себе. Рекурсия прекращается когда счетчик <em>count</em>  становится равен нулю.  На каждом шаге производится вывод значения входной строки  <em>input.</em></p>
<p>Алгоритм очень небыстрый. Количество итераций для решения этой задачи можно существенно сократить, если переписать алгоритм следующим образом:</p>

<table class="code">
    <tbody>
        <tr>
            <td><!--php-->
            <pre class="brush: plain">&lt;xsl:template match="*"&gt;

       &lt;xsl:call-template name="duplicate"&gt;            
           &lt;xsl:with-param name="count"&gt;              3&lt;/xsl:with-param&gt;            
           &lt;xsl:with-param name="input"&gt;              Ой!&lt;/xsl:with-param&gt;            
       &lt;/xsl:call-template&gt;            

   &lt;/xsl:template&gt;            

   &lt;xsl:template name="duplicate"&gt;            
       &lt;xsl:param name="count" select="1"/&gt;            
       &lt;xsl:param name="input"/&gt;            

       &lt;xsl:choose&gt;            

           &lt;xsl:when test="not($count) or not($input)"/&gt;            

           &lt;xsl:when test="$count = 1"&gt;            
               &lt;xsl:value-of select="$input"/&gt;            
           &lt;/xsl:when&gt;            

           &lt;xsl:otherwise&gt;            

               &lt;xsl:if test="$count mod 2"&gt;            
                   &lt;xsl:value-of select="$input"/&gt;            
               &lt;/xsl:if&gt;            

               &lt;xsl:call-template name="duplicate"&gt;            
                   &lt;xsl:with-param name="input" select="concat($input,$input)"/&gt;            
                   &lt;xsl:with-param name="count" select="floor($count div 2)"/&gt;            
               &lt;/xsl:call-template&gt;            

           &lt;/xsl:otherwise&gt;            

       &lt;/xsl:choose&gt;            

   &lt;/xsl:template&gt;            
</pre><!--/php--></td>
        </tr>
    </tbody>
</table>
<p>В этом примере на каждом шаге итерации происходит склейка входной строки и уменьшение счетчика в два раза. Цикл повторяется до тех пор, пока счетчик не станет равен нулю.</p>
<h2>Пример III. XSLT-reverse. Вывод строки в обратном порядке</h2>
<p>По существу пример мало чем отличается от предыдущего или рассмотренного ранее примера III в выпуске 3 (<a href="/article/23">формирование алфавитных указателей</a>)<br/>
Тот же самый цикл при помощи рекурсии. И входная строка, от которой на каждой итерации отщипывается последняя буква, до тех пор, пока строка не станет пустой. </p>
<p> </p>

<table class="code">
    <tbody>
        <tr>
            <td><!--php-->
            <pre class="brush: plain">&lt;xsl:template match="*"&gt;

       &lt;xsl:call-template name="reverse"&gt;            
           &lt;xsl:with-param name="input" select="123456789"/&gt;            
       &lt;/xsl:call-template&gt;            

   &lt;/xsl:template&gt;            

   &lt;xsl:template name="reverse"&gt;            
       &lt;xsl:param name="input"&gt;              12345&lt;/xsl:param&gt;            
       &lt;xsl:param name="size" select="string-length($input)"/&gt;            

       &lt;xsl:choose&gt;            
           &lt;xsl:when test="$size &lt;1"/&gt;            
           &lt;xsl:otherwise&gt;            

               &lt;xsl:value-of select="substring($input,$size,1)"/&gt;            
              
               &lt;!--  рекусивный вызов  --&gt;            
               &lt;xsl:call-template name="reverse"&gt;            
                   &lt;xsl:with-param name="input" select="$input"/&gt;            
                   &lt;xsl:with-param name="size" select="$size - 1"/&gt;            
               &lt;/xsl:call-template&gt;            
  
           &lt;/xsl:otherwise&gt;            
       &lt;/xsl:choose&gt;            
  
   &lt;/xsl:template&gt;              </pre><!--/php--></td>
        </tr>
    </tbody>
</table>
<p>Алгоритм так же можно существенно ускорить, если входную строку разбить на две части и для каждой из частей запустить свою рекурсию:</p>

<table class="code">
    <tbody>
        <tr>
            <td><!--php-->
            <pre class="brush: plain">&lt;xsl:template name="reverse"&gt;
      
       &lt;xsl:param name="input"/&gt;            
       &lt;xsl:variable name="size" select="string-length($input)"/&gt;            
      
       &lt;xsl:choose&gt;            
         
           &lt;!--  вывод строки из одного символа --&gt;            
           &lt;xsl:when test="$size &lt;2"&gt;            
               &lt;xsl:value-of select="$input"/&gt;            
           &lt;/xsl:when&gt;            
          
           &lt;!--  вывод реверса строки из двух символов --&gt;            
           &lt;xsl:when test="$size = 2"&gt;            
               &lt;xsl:value-of select="substring($input,2,1)"/&gt;            
               &lt;xsl:value-of select="substring($input,1,1)"/&gt;            
           &lt;/xsl:when&gt;            
          
           &lt;xsl:otherwise&gt;            
               &lt;!--  входная строка больше двух символов 
               разбивается пополам --&gt;            
               &lt;xsl:variable name="center" select="floor($size div 2)"/&gt;            
              
               &lt;!--  запускается отдельная  рекурсия 
               по второй половине строки  --&gt;            
               &lt;xsl:call-template name="reverse"&gt;            
                   &lt;xsl:with-param name="input" 
                     select="substring($input,$center+1,$center+1)"/&gt;            
               &lt;/xsl:call-template&gt;            
              
               &lt;!--  запускается отдельная  рекурсия 
               по первой половине строки  --&gt;            
               &lt;xsl:call-template name="reverse"&gt;            
                   &lt;xsl:with-param name="input" 
                   select="substring($input,1,$center)"/&gt;            
               &lt;/xsl:call-template&gt;            
              
           &lt;/xsl:otherwise&gt;            
       &lt;/xsl:choose&gt;            
      
   &lt;/xsl:template&gt;              </pre><!--/php--></td>
        </tr>
    </tbody>
</table>
<h2>Пример IV. XSLT-replace. Замена подстроки</h2>
<p> В этом примере реализуется функция аналогичная php функции str_replace().</p>
<p>В качестве функции используется именованный шаблон  <em>replace</em>. В нем задается входная строка <em>input</em>, образец поиска <em>from</em> и образец замены <em>to</em>.</p>
<p>В шалоне определяется первое вхождение образца поиска во входной строке. Затем строка ращепляется на две с использованием встроенных функций  <em>substring-before()</em> и <em>substring-after</em>(). Первая часть вместе с образом замены выводится в выходной поток, а вторая часть - подается в качестве входной для итерационного вызова шаблона <em>replace.</em></p>

<table class="code">
    <tbody>
        <tr>
            <td><!--php-->
            <pre class="brush: plain">   &lt;xsl:template match="/"&gt;
       &lt;xsl:call-template name="replace"&gt;            
           &lt;xsl:with-param name="input" select="'Путин - наш президент. Путин - наше Все'"/&gt;            
           &lt;xsl:with-param name="from" select="'Путин'"/&gt;            
           &lt;xsl:with-param name="to" select="'Медведев'"/&gt;            
       &lt;/xsl:call-template&gt;            
   &lt;/xsl:template&gt;            


   &lt;xsl:template name="replace"&gt;            
       &lt;xsl:param name="input"/&gt;            
       &lt;xsl:param name="from"/&gt;            
       &lt;xsl:param name="to"/&gt;            

       &lt;xsl:choose&gt;            
           &lt;xsl:when test="contains($input, $from)"&gt;            
               &lt;!--   вывод подстроки предшествующей образцу  + вывод строки замены --&gt;            
               &lt;xsl:value-of select="substring-before($input, $from)"/&gt;            
               &lt;xsl:value-of select="$to"/&gt;            


               &lt;!--   вход в итерацию --&gt;            
               &lt;xsl:call-template name="replace"&gt;            
                   &lt;!--  в качестве входного параметра задается подстрока после образца замены  --&gt;            
                   &lt;xsl:with-param name="input" select="substring-after($input, $from)"/&gt;            
                   &lt;xsl:with-param name="from" select="$from"/&gt;            
                   &lt;xsl:with-param name="to" select="$to"/&gt;            
               &lt;/xsl:call-template&gt;            

           &lt;/xsl:when&gt;            
           &lt;xsl:otherwise&gt;            
               &lt;xsl:value-of select="$input"/&gt;            
           &lt;/xsl:otherwise&gt;            
       &lt;/xsl:choose&gt;            
   &lt;/xsl:template&gt;              </pre><!--/php--></td>
        </tr>
    </tbody>
</table>
<p>Как обычно, все исходники можно найти в архиве <a href="/doc/xslt-examples.zip">xslt-examples.zip</a> в директории ex4</p>
<h2>ЗЫ</h2>
<p><img width="180" align="right" style="margin: 0pt 0pt 10px 10px;" src="http://oreilly.com/catalog/covers/0596009747_cat.gif" alt="Salvatore Mangano&#xA0;XSLT Cookbook, Second Edition (O'Reilly)"/>Примеры "быстрых" алгоритмов взяты из великолепной книги   <a href="#" title="www.amazon.com/gp/product/0596009747?ie=UTF8&amp;tag=xmlnewsandres-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596009747" rel="nofollow" class="external">Salvatore Mangano "XSLT Cookbook, Second Edition</a>" В мае этого года она <a href="#" title="www.bolero.ru/books/9785977502924.html?terms=xslt" rel="nofollow" class="external">вышла в русском переводе</a> и   до сих пор продается в онлайновых магазинах. Издана мизерным тиражом (2.000 экземпляров) и через год  станет таким же раритетом,  как и любая мало-мальски приличная приличная книги по XSLT</p>
<p>Жаль только, что при переиздании в русской версии была потеряна легко узнаваемая фирменная обложка  издательства <a href="#" title="oreilly.com" rel="nofollow" class="external">O'Reilly</a> Вместо нее  "дизайнер"   Мовчан А.Г.,   налепил какое-то несусветное говно в традиционном для издательства <a href="#" title="bhv.ru/" rel="nofollow" class="external">bhv </a>стиле.  Впрочем, оригинальную  версию  изданную в <a href="#" title="oreilly.com" rel="nofollow" class="external">O'Reilly</a>  я купил еще год назад.</p></div>
          <ol class="tags big">
            <li class="date">2008-11-10</li>
            <li>
              <a href="/xslt-recursive">xslt-recursive</a>
            </li>
            <li>
              <a href="/xslt-examples">xslt-examples</a>
            </li>
          </ol>
          <ol class="see">
            <li>
              <a href="#"><span>incrementaldevelopment.com/xsltrick/</span> - <b>Gallery of Stupid XSL and XSLT Tricks</b></a>
            </li>
            <li>
              <a href="#"><span>snippets.dzone.com/tag/xslt</span> - <b>DZone Snippets: XSLT examples</b></a>
            </li>
            <li>
              <a href="#"><span>www.dpawson.co.uk/xsl/sect2/sect21.html</span> - <b>XSLT Questions and Answers - FAQ.</b></a>
            </li>
          </ol>
          <ul class="comment">
            <li id="a150" title="a0">
              <a name="&#x411;&#x43E;&#x43B;&#x44C;&#x448;&#x43E;&#x439; &#x411;&#x440;&#x430;&#x442;" title="www.fsb.ru" rel="10.11.08"/>
              <div>Сомнительные примеры. Вы что-то имеете против?</div>
            </li>
            <li id="a151" title="a150">
              <a name="&#x418;&#x441;&#x430;&#x430;&#x43A; &#x422;&#x44B;&#x43D;&#x433;&#x44B;&#x43B;&#x447;&#x430;&#x432;" title="erum.ru" rel="10.11.08"/>
              <div>Я же просил, писать сюда только что-то разумное.</div>
            </li>
            <li id="a153" title="a0">
              <a name="H.Sapiens" title="" rel="10.11.08"/>
              <div>Ошибка у вас.  В XSLT2 многие вещи включены, которых в XSLT1 не было. Например поддержка  регулярных выражений. В остальных выпусках то же самое.</div>
            </li>
            <li id="a155" title="a153">
              <a name="&#x418;&#x441;&#x430;&#x430;&#x43A; &#x422;&#x44B;&#x43D;&#x433;&#x44B;&#x43B;&#x447;&#x430;&#x432;" title="erum.ru" rel="10.11.08"/>
              <div>Сорри. Я в первых примерах это упомянул, а здесь забыл. Спасибо за замечание. Поправлю.<br/>Беда в том, что  XSLT2 ждать долго придется. Пока вроде толька Java  его поддерживает? Во всяком случае даже в последней VS его нет. Это уж не говоря про PHP. Там он лет через пять появится.  А так конечно красиво.</div>
            </li>
          </ul>
        </div>
      </div>
      <div class="R">
        <a href="/" title="&#x41D;&#x430; &#x433;&#x43B;&#x430;&#x432;&#x43D;&#x443;&#x44E;"/>
      </div>
    </div>
    <div id="li"/>
  </body>
</html>

