Количество слов в XSLT 1.0 с HTML - PullRequest
3 голосов
/ 20 августа 2010

Я ищу шаблон, который урезает поле до 30 слов.Однако это поле содержит HTML, и HTML не должен считаться словом.

Ответы [ 2 ]

2 голосов
/ 20 августа 2010

Попробуйте, хотя по общему признанию вызов translate немного уродлив:

<xsl:template match="field">
  <xsl:value-of select="string-length(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',''))+1" />
</xsl:template>

Это, конечно, требует, чтобы строка в вызове перевода включала все символы, которые могли появиться в поле, кроме пробелов. Сначала он вызывает normalize-space(.), чтобы убрать как двойные пробелы, так и все, кроме текстового содержимого. Затем он удаляет все, кроме пробелов, считает длину полученной строки и добавляет ее. Это означает, что если у вас есть <p>My<b>text</b> test</p>, это будет считаться как 2, так как Mytext будет считаться одним словом.

Если вам нужно более надежное решение, оно немного более замысловатое:

<xsl:template match="field">
  <xsl:call-template name="countwords">
    <xsl:with-param name="text" select="normalize-space(.)" />
  </xsl:call-template>
</xsl:template>

<xsl:template name="countwords">
  <xsl:param name="count" select="0" />
  <xsl:param name="text" />
  <xsl:choose>
    <xsl:when test="contains($text,' ')">
      <xsl:call-template name="countwords">
        <xsl:with-param name="count" select="$count + 1" />
        <xsl:with-param name="text" select="substring-after($text,' ')" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="$count + 1" /></xsl:otherwise>
  </xsl:choose>
</xsl:template>

Это передает результат normalize-space(.) в рекурсивный именованный шаблон, который вызывает себя, когда в $text есть пробел, увеличивает его параметр count и отбирает первое слово каждый раз, используя вызов substring-after($text,' '). Если пробела нет, он обрабатывает $text как одно слово и просто возвращает $count + 1 (+1 для текущего слова).

Имейте в виду, что это будет включать ВСЕ текстовое содержимое в поле, включая содержимое внутренних элементов.

РЕДАКТИРОВАТЬ: Примечание для себя: прочитайте вопрос правильно, только заметил, что вам нужно больше, чем просто подсчет слов. Это значительно сложнее сделать, если вы хотите включить какие-либо теги xml, но небольшая модификация вышеперечисленного - это все, что нужно, чтобы выплевывать каждое слово, а не просто считать их:

<xsl:template name="countwords">
  <xsl:param name="count" select="0" />
  <xsl:param name="text" />
  <xsl:choose>
    <xsl:when test="$count = 30" />
    <xsl:when test="contains($text,' ')">
      <xsl:if test="$count != 0"><xsl:text>&#32;</xsl:text></xsl:if>
      <xsl:value-of select="substring-before($text,' ')" />
      <xsl:call-template name="countwords">
        <xsl:with-param name="count" select="$count + 1" />
        <xsl:with-param name="text" select="substring-after($text,' ')" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="$text" /></xsl:otherwise>
  </xsl:choose>
</xsl:template>

Есть дополнительное предложение <xsl:when, чтобы просто прекратить рекурсию, когда число достигает 30, и рекурсивное предложение выводит текст после добавления пробела в начале, если это не было первое слово.

РЕДАКТИРОВАТЬ: Хорошо, вот решение, которое сохраняет экранированный контент XML:

<xsl:template match="field">
  <xsl:call-template name="countwords">
    <xsl:with-param name="text" select="." />
  </xsl:call-template>
</xsl:template>

<xsl:template name="countwords">
  <xsl:param name="count" select="0" />
  <xsl:param name="text" />
  <xsl:choose>
    <xsl:when test="starts-with($text, '&lt;')">
      <xsl:value-of select="concat(substring-before($text,'&gt;'),'&gt;')" />
      <xsl:call-template name="countwords">
        <xsl:with-param name="count">
          <xsl:choose>
            <xsl:when test="starts-with(substring-after($text,'&gt;'),' ')"><xsl:value-of select="$count + 1" /></xsl:when>
            <xsl:otherwise><xsl:value-of select="$count" /></xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
        <xsl:with-param name="text" select="substring-after($text,'&gt;')" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="(contains($text, '&lt;') and contains($text, ' ') and string-length(substring-before($text,' ')) &lt; string-length(substring-before($text,'&lt;'))) or (contains($text,' ') and not(contains($text,'&lt;')))">
      <xsl:choose>
        <xsl:when test="$count &lt; 29"><xsl:value-of select="concat(substring-before($text, ' '),'&#32;')" /></xsl:when>
        <xsl:when test="$count = 29"><xsl:value-of select="substring-before($text, ' ')" /></xsl:when>
      </xsl:choose>
      <xsl:call-template name="countwords">
        <xsl:with-param name="count">
          <xsl:choose>
            <xsl:when test="normalize-space(substring-before($text, ' ')) = ''"><xsl:value-of select="$count" /></xsl:when>
            <xsl:otherwise><xsl:value-of select="$count + 1" /></xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
        <xsl:with-param name="text" select="substring-after($text,' ')" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="(contains($text, '&lt;') and contains($text, ' ') and string-length(substring-before($text,' ')) &gt; string-length(substring-before($text,'&lt;'))) or contains($text,'&lt;')">
      <xsl:if test="$count &lt; 30">
        <xsl:value-of select="substring-before($text, '&lt;')" />
      </xsl:if>
      <xsl:call-template name="countwords">
        <xsl:with-param name="count" select="$count" />
        <xsl:with-param name="text" select="concat('&lt;',substring-after($text,'&lt;'))" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="$count &lt; 30">
        <xsl:value-of select="$text" />
      </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Если вам нужно что-то объяснить лучше, дайте мне знать, я бы не стал вдаваться в подробности, если вам это не нужно!

0 голосов
/ 07 февраля 2012

Вот немного другой подход:

Если вы можете очистить свой ввод, чтобы получить нормализованную строку текста, который вы хотите подсчитать, вы можете сравнить длину строки строки с пробелами, чтобыдлина строки с удаленными пробелами.Разница должна заключаться в количестве ваших слов.

Функция подсчета слов (шаблон) будет выглядеть примерно так:

<xsl:template name="wordCount">
    <xsl:param name="input" required="yes"/>
    <xsl:param name="sep" select="'‒–—―'"/>
    <xsl:variable name="big"><xsl:value-of select="normalize-space(translate($input, $sep, ' '))"/></xsl:variable>
    <xsl:variable name="small"><xsl:value-of select="translate($big, ' ', '')"/></xsl:variable>
    <xsl:value-of select="string-length($big)-string-length($small)"/>
</xsl:template>

Параметр $ sep позволяет вам определить список любого символа (s) (а также пробел), который вы хотите считать в качестве разделителя слов.

Затем вы можете использовать конструктор последовательности при вызове шаблона для построения нужной строки (я оставлю этов качестве упражнения для читателя):

<xsl:call-template name="wordCount">
    <xsl:with-param name="input">
        <!-- templates etc to output text from html -->
    </xsl:with-param>
</xsl:call-template>
...