xquery перенос слов - PullRequest
       7

xquery перенос слов

5 голосов
/ 22 сентября 2010

Мне нужно обернуть слово длинной строкой до 100 символов, используя XQuery.Другими словами, чтобы заменить некоторые пробелы на новые строки, чтобы непатологические строки были короче, чем 100 символов.Есть ли простой способ?

Ответы [ 4 ]

9 голосов
/ 08 октября 2010

Я думаю, вам просто нужно это выражение XPath 2.0:

replace(concat(normalize-space(text),' '),'(.{0,60}) ','$1
')

С вводом Димитра, вывод:

Dec. 13 — As always for a presidential inaugural, security
and surveillance were extremely tight in Washington, DC,
last January. But as George W. Bush prepared to take the
oath of office, security planners installed an extra layer
of protection: a prototype software system to detect a
biological attack. The U.S. Department of Defense, together
with regional health and emergency-planning agencies,
distributed a special patient-query sheet to military
clinics, civilian hospitals and even aid stations along the
parade route and at the inaugural balls. Software quickly
analyzed complaints of seven key symptoms — from rashes to
sore throats — for patterns that might indicate the early
stages of a bio-attack. There was a brief scare: the system
noticed a surge in flulike symptoms at military clinics.
Thankfully, tests confirmed it was just that — the flu.
1 голос
/ 22 сентября 2010

Я не знаю XQuery достаточно хорошо, чтобы выразить это решение XSLT в XQuery, но я думаю, что просто предоставление его может быть полезным.

Обратите внимание, что в типичных случаях реального мира слова отделяются болеечем один разделитель.Следующее решение обрабатывает в качестве разделителей все символы, указанные в параметре, и разбивает каждую строку на границе максимальной длины.Он является частью библиотеки функций / шаблонов FXSL для XSLT 1.0 или 2.0.

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:ext="http://exslt.org/common"
xmlns:str-split2lines-func="f:str-split2lines-func"
exclude-result-prefixes="xsl f ext str-split2lines-func"
>


   <xsl:import href="dvc-str-foldl.xsl"/>

   <str-split2lines-func:str-split2lines-func/>

   <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="/">
      <xsl:call-template name="str-split-to-lines">
        <xsl:with-param name="pStr" select="/*"/>
        <xsl:with-param name="pLineLength" select="60"/>
        <xsl:with-param name="pDelimiters" select="' &#9;&#10;&#13;'"/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template name="str-split-to-lines">
      <xsl:param name="pStr"/>
      <xsl:param name="pLineLength" select="60"/>
      <xsl:param name="pDelimiters" select="' &#9;&#10;&#13;'"/>

      <xsl:variable name="vsplit2linesFun"
                    select="document('')/*/str-split2lines-func:*[1]"/>

      <xsl:variable name="vrtfParams">
       <delimiters><xsl:value-of select="$pDelimiters"/></delimiters>
       <lineLength><xsl:copy-of select="$pLineLength"/></lineLength>
      </xsl:variable>

      <xsl:variable name="vResult">
          <xsl:call-template name="dvc-str-foldl">
            <xsl:with-param name="pFunc" select="$vsplit2linesFun"/>
            <xsl:with-param name="pStr" select="$pStr"/>
            <xsl:with-param name="pA0" select="ext:node-set($vrtfParams)"/>
          </xsl:call-template>
      </xsl:variable>
      <xsl:for-each select="ext:node-set($vResult)/line">
        <xsl:for-each select="word">
          <xsl:value-of select="concat(., ' ')"/>
        </xsl:for-each>
        <xsl:value-of select="'&#xA;'"/>
      </xsl:for-each>
    </xsl:template>

    <xsl:template match="str-split2lines-func:*" mode="f:FXSL">
      <xsl:param name="arg1" select="/.."/>
      <xsl:param name="arg2"/>

      <xsl:copy-of select="$arg1/*[position() &lt; 3]"/>
      <xsl:copy-of select="$arg1/line[position() != last()]"/>

      <xsl:choose>
        <xsl:when test="contains($arg1/*[1], $arg2)">
          <xsl:if test="string($arg1/word) or string($arg1/line/word)">
             <xsl:call-template name="fillLine">
               <xsl:with-param name="pLine" select="$arg1/line[last()]"/>
               <xsl:with-param name="pWord" select="$arg1/word"/>
               <xsl:with-param name="pLineLength" select="$arg1/*[2]"/>
             </xsl:call-template>
          </xsl:if>
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy-of select="$arg1/line[last()]"/>
          <word><xsl:value-of select="concat($arg1/word, $arg2)"/></word>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

      <!-- Test if the new word fits into the last line -->
    <xsl:template name="fillLine">
      <xsl:param name="pLine" select="/.."/>
      <xsl:param name="pWord" select="/.."/>
      <xsl:param name="pLineLength" />

      <xsl:variable name="vnWordsInLine" select="count($pLine/word)"/>
      <xsl:variable name="vLineLength" 
       select="string-length($pLine) + $vnWordsInLine"/>
      <xsl:choose>
        <xsl:when test="not($vLineLength + string-length($pWord) 
                           > 
                            $pLineLength)">
          <line>
            <xsl:copy-of select="$pLine/*"/>
            <xsl:copy-of select="$pWord"/>
          </line>
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy-of select="$pLine"/>
          <line>
            <xsl:copy-of select="$pWord"/>
          </line>
          <word/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

, когда это преобразование применяется к следующему документу XML :

<text>
Dec. 13 — As always for a presidential inaugural, security and surveillance were
extremely tight in Washington, DC, last January. But as George W. Bush prepared to
take the oath of office, security planners installed an extra layer of protection: a
prototype software system to detect a biological attack. The U.S. Department of
Defense, together with regional health and emergency-planning agencies, distributed
a special patient-query sheet to military clinics, civilian hospitals and even aid
stations along the parade route and at the inaugural balls. Software quickly
analyzed complaints of seven key symptoms — from rashes to sore throats — for
patterns that might indicate the early stages of a bio-attack. There was a brief
scare: the system noticed a surge in flulike symptoms at military clinics.
Thankfully, tests confirmed it was just that — the flu.
</text>

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

Dec. 13 — As always for a presidential inaugural, security 
and surveillance were extremely tight in Washington, DC, 
last January. But as George W. Bush prepared to take the 
oath of office, security planners installed an extra layer 
of protection: a prototype software system to detect a 
biological attack. The U.S. Department of Defense, together 
with regional health and emergency-planning agencies, 
distributed a special patient-query sheet to military 
clinics, civilian hospitals and even aid stations along the 
parade route and at the inaugural balls. Software quickly 
analyzed complaints of seven key symptoms — from rashes to 
sore throats — for patterns that might indicate the early 
stages of a bio-attack. There was a brief scare: the system 
noticed a surge in flulike symptoms at military clinics. 
Thankfully, tests confirmed it was just that — the flu. 
1 голос
/ 22 сентября 2010

Если ваша строка длиннее 100 символов, вы хотите заменить последний набор последовательных пробелов в первых 101 символе новой строкой. Если в первых 101 символах пробела нет, вам нужно просто заменить первый пробел в строке. Затем вы можете применить ту же логику рекурсивно к оставшимся строкам строки

.

Это можно сделать так:

declare function local:wrap-line($str)
{
  if (string-length($str) < 100 or not(contains($str," ")))
  then $str
  else 
    let $wrapped := if (contains(substring($str,1,101)," "))
                    then replace($str, "^(.{0,99}[^ ]) +", "$1&#10;")
                    else replace($str, "^(.*?) ", "$1&#10;")
    return concat(substring-before($wrapped, "&#10;"), "&#10;",
                  local:wrap-line(substring-after($wrapped, "&#10;")))
};

declare function local:word-wrap($str)
{
  string-join(
    for $line in tokenize($str,"&#10;")
    return local:wrap-line($line),
    "&#10;")
};

Это работает, поскольку (.{0,99}[^ ]) + соответствует самой длинной возможной серии из 100 символов, не заканчивающейся пробелом, за которым следует количество пробелов, а (.*?) соответствует самой короткой возможной серии символов, заканчивающейся пробелом.

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

0 голосов
/ 12 февраля 2018

Попробуйте использовать нижеприведенную функцию XQuery: она может переносить любой текст любым словом.

Параметры для передачи: 1. $ Text - перенос текста в слова 2. 50-Word Wrap длина строки 3 " "- после переноса слова - символ новой строки 4. 0 - Начальная позиция

declare function xf:wrap-string($str as xs:string,
                                $wrap-col as xs:integer,
                                $break-mark as xs:string,
                                $pos as xs:integer)
    as xs:string {
       if(fn:contains( $str, ' ' )) then 
        let $first-word := fn:substring-before( $str, ' ' )
        let $pos-now := xs:integer($pos + 1 + string-length($first-word))
        return
            if ($pos>0 and $pos-now>=$wrap-col) then
                 concat($break-mark,
                 xf:wrap-string($str,$wrap-col,$break-mark,xs:integer(0)))
            else
                concat($first-word,' ',
                 xf:wrap-string(substring-after( $str, ' ' ),
                               $wrap-col,
                               $break-mark,
                               $pos-now))
       else(
        if ($pos+string-length($str)>$wrap-col) then
            concat($break-mark,$str)
        else ($str)
       )
};

    declare function xf:wrap-test($str as xs:string,
                                    $wrap-col as xs:integer,
                                    $break-mark as xs:string,
                                    $pos as xs:integer)
     as xs:string {
            let $Result := xf:wrap-string($str,$wrap-col,$break-mark,$pos)
            return
            if (fn:contains($Result,'&#10;')) then
                fn:string-join(for $line in tokenize($Result, '&#10;')
                                return
                                if (fn:string-length($line)<50) then
                                    xf:pad-string-to-length(string($line),50)
                                else ($line),
                '')
            else $Result 
     }; 

    xf:wrap-test($Text,50,"&#10;",0)

Parameters to pass: 
1. $Text - Text to word wrap
2. 50 -Word wrap string length
3. "&#10;" - After word wrap - new line character
4. 0 - Starting position
...