XSLT поиск и замена знака препинания

У меня есть XSLT-каскад, передающий XML в TeX. На последнем этапе у меня есть простой XML-файл со всем текстом между двумя тегами, и я хочу применить несколько процедур поиска и замены.

Таким образом, входной файл выглядит следующим образом:

<start>
    .–
    ,–
    {– 
</start>

при применении с этим XSLT (более или менее дословно взято из Замена строк в различных файлах XML)

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:param name="list">
        <words>
             <word>
            <search> / </search>
            <replace>\allowbreak\,\slash\,\allowbreak{}</replace>
        </word>
        <word>
            <search>.–</search>
            <replace>{\dotdash}</replace>
        </word>
        <word>
            <search>,–</search>
            <replace>{\commadash}</replace>
        </word>
        <word>
            <search>;–</search>
            <replace>{\semicolondash}</replace>
        </word>
        <word>
            <search>!–</search>
            <replace>{\excdash}</replace>
        </word>
        </words>
    </xsl:param>
    
    <xsl:template match="@*|*|comment()|processing-instruction()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="text()">
        <xsl:variable name="search" select="concat('(',string-join($list/words/word/search,'|'),')')"/>
        <xsl:analyze-string select="." regex="{$search}">
            <xsl:matching-substring>
                <xsl:value-of select="$list/words/word[search=current()]/replace"/>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
</xsl:stylesheet>

Должен иметь следующий вывод:

\точечный{}

\commadash{}

{–

К сожалению, {– кажется, что-то вызывает и исчезает. Кто-нибудь может объяснить, почему?


person martinanton    schedule 31.01.2017    source источник


Ответы (1)


Рад, что оригинальный ответ, на который вы ссылаетесь, помог. Пожалуйста, рассмотрите возможность голосования, если вы еще этого не сделали. ;-)

Проблема в том, что . является особенным в регулярном выражении. Таким образом, <search>.–</search> будет соответствовать любому символу, за которым следует -.

Вы должны избежать . в вашей поисковой переменной:

<xsl:variable name="search" select="replace(concat('(',string-join($list/words/word/search,'|'),')'),'\.','\\.')"/>

Вам также нужно будет экранировать любые другие специальные символы регулярных выражений, поэтому вы можете подумать о создании xsl:function, чтобы упростить эту часть.

Вот пример функции, которая экранирует . и { для начала...

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:so="stackoverflow example" exclude-result-prefixes="so">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:param name="list">
    <words>
      <word>
        <search> / </search>
        <replace>\allowbreak\,\slash\,\allowbreak{}</replace>
      </word>
      <word>
        <search>.–</search>
        <replace>{\dotdash}</replace>
      </word>
      <word>
        <search>,–</search>
        <replace>{\commadash}</replace>
      </word>
      <word>
        <search>;–</search>
        <replace>{\semicolondash}</replace>
      </word>
      <word>
        <search>!–</search>
        <replace>{\excdash}</replace>
      </word>
      <!--<word>
        <search>{–</search>
        <replace>bam!</replace>
      </word>-->
    </words>
  </xsl:param>

  <xsl:function name="so:escapeRegex">
    <xsl:param name="regex"/>
    <xsl:analyze-string select="$regex" regex="\.|\{{">
      <xsl:matching-substring>
        <xsl:value-of select="concat('\',.)"/>
      </xsl:matching-substring>
      <xsl:non-matching-substring>
        <xsl:value-of select="."/>
      </xsl:non-matching-substring>
    </xsl:analyze-string>
  </xsl:function>

  <xsl:template match="@*|*|comment()|processing-instruction()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:variable name="search" select="so:escapeRegex(concat('(',string-join($list/words/word/search,'|'),')'))"/>
    <xsl:analyze-string select="." regex="{$search}">
      <xsl:matching-substring>
        <xsl:message>"<xsl:value-of select="."/>" matched <xsl:value-of select="$search"/></xsl:message>
        <xsl:value-of select="$list/words/word[search=current()]/replace"/>
      </xsl:matching-substring>
      <xsl:non-matching-substring>
        <xsl:value-of select="."/>
      </xsl:non-matching-substring>
    </xsl:analyze-string>
  </xsl:template>
</xsl:stylesheet>

Если вы раскомментируете последний word в параметре list, он заменит {– в вашем примере.

person Daniel Haley    schedule 31.01.2017
comment
В XSLT 3.0 вы можете использовать flags=q, чтобы указать, что все символы в регулярном выражении должны рассматриваться как представляющие самих себя. Но, конечно, вы не можете использовать | для разделения альтернатив. - person Michael Kay; 31.01.2017