Я хочу вызвать шаблон, который урежет поле до 30 слов. Однако это поле содержит HTML, и HTML не должен считаться словом.
Количество слов XSLT 1.0 с HTML
Ответы (2)
Попробуйте это, хотя, по общему признанию, вызов перевода немного уродлив:
<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> </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
, чтобы просто остановить рекурсию, когда count достигает 30, а предложение recursive выводит текст после добавления пробела в начале, если это не было первое слово.
РЕДАКТИРОВАТЬ: Хорошо, вот решение, которое сохраняет экранированное содержимое 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, '<')">
<xsl:value-of select="concat(substring-before($text,'>'),'>')" />
<xsl:call-template name="countwords">
<xsl:with-param name="count">
<xsl:choose>
<xsl:when test="starts-with(substring-after($text,'>'),' ')"><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,'>')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="(contains($text, '<') and contains($text, ' ') and string-length(substring-before($text,' ')) < string-length(substring-before($text,'<'))) or (contains($text,' ') and not(contains($text,'<')))">
<xsl:choose>
<xsl:when test="$count < 29"><xsl:value-of select="concat(substring-before($text, ' '),' ')" /></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, '<') and contains($text, ' ') and string-length(substring-before($text,' ')) > string-length(substring-before($text,'<'))) or contains($text,'<')">
<xsl:if test="$count < 30">
<xsl:value-of select="substring-before($text, '<')" />
</xsl:if>
<xsl:call-template name="countwords">
<xsl:with-param name="count" select="$count" />
<xsl:with-param name="text" select="concat('<',substring-after($text,'<'))" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$count < 30">
<xsl:value-of select="$text" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Если вам нужно что-то из этого объяснить лучше, дайте мне знать, я бы не хотел вдаваться в подробности, если вам это не нужно!
normalize-space(.)
на normalize-space(translate(., ',:;.', ' '))
, либо добавить в шаблон отдельное предложение <xsl:when>
для каждого разделителя. Однако, если честно, если вам нужен такой уровень синтаксического анализа, я бы не рекомендовал делать это с помощью xpath/xslt.
- person Flynn1179; 20.08.2010
<field> <html> <p>This</p><b>i</b>s it. </html> </field>
он выдает: `Это оно.`, но желаемый результат (для ограничения в два слова): <field> <html> <p>This</p><b>i</b>s </html> </field>
Таким образом, есть по крайней мере две проблемы: 1. Неправильный подсчет слов; 2. Потеря разметки. Итак, еще раз, ваш ответ до сих пор не то, что имелось в виду и хотел - пожалуйста, рассмотрите возможность предоставления соответствующего ответа или удаления этого.
- person Dimitre Novatchev; 20.08.2010
<
) закодированы, элементы блочного стиля имеют явное пространство (внутри или следующего элемента) между ними и следующим словом. Кроме того, если он должен сохранить кодировку HTML, это может быть недействительным.
- person ; 20.08.2010
<field><p>Monday</p><b>T</b>uesday</field>
результат будет следующим: <p>Monday</p><b>T</b>uesday
Думаю, правильный результат должен быть: <p>Monday
pMonday/pbT/buesday
- person Dimitre Novatchev; 20.08.2010
Здесь немного другой подход:
Если вы можете очистить свой ввод, чтобы получить нормализованную строку текста, который вы хотите подсчитать, вы можете сравнить длину строки строки с пробелами с длиной строки строки с удаленными пробелами. Разница должна заключаться в количестве слов.
Функция подсчета слов (шаблон) будет выглядеть примерно так:
<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 позволяет вам определить список любых символов (а также пробелов), которые вы хотите считать разделителями слов.
Затем вы можете использовать конструктор последовательности при вызове шаблона для создания нужной строки (я оставлю это в качестве упражнения для читателя):
<xsl:call-template name="wordCount">
<xsl:with-param name="input">
<!-- templates etc to output text from html -->
</xsl:with-param>
</xsl:call-template>
<field><i> etc..
в вашем xml или<field><i>etc..
? - person Flynn1179   schedule 20.08.2010field><i>
вместо<field><i>
), тогда вам нужна XSLT-реализация синтаксического анализатора XML (я думаю, мне может потребоваться больше, чем просто какое-то время. Я не знаю о Димитре. ..) или нестандартное решение (возможно, с RTF плюсnode-set()
). В противном случае решение было бы очень простым. - person   schedule 20.08.2010just make sure that whenever you see a <, you copy everything up to the next > as is
. Ну, это была бы минимальная реализация парсера! - person   schedule 20.08.2010<field><i> This < is less than sign!<i><field>
- person   schedule 20.08.2010<i>This < is less than sign</i>
недействителен. - person Flynn1179   schedule 20.08.2010<i>This < is less than sign</i>
будет кодироваться как<i> This &lt; is less than sign!<i>
. Но тогда, возможно, при необходимости вам нужно будет добавить декодирование сущности... - person   schedule 20.08.2010fn:parse
. Кроме того, я проверил свою идею создания RTF с помощью DOE, а затем использовал функцию расширенияnode-set
. я не работала... - person   schedule 20.08.2010