Токенизация XSLT с помощью регулярного выражения для токенизации только в том случае, если за точкой с запятой не следует пробел и число

Я пытаюсь токенизировать эту строку, чтобы создавать отдельные записи для каждой библиографической ссылки. Загвоздка в том, что иногда точка с запятой отделяет библиографическую статью, а иногда — номера страниц. Я хочу написать токенизатор только для токенизации, если за точкой с запятой не следует пробел и число. То, что у меня есть ниже, вроде как работает, но вырезает первую букву каждой цитаты. (Я использую XSLT 2.0)

Вход:

  <zotero>(Leppin 2019; Francisco 2011, 119; van Ginkel 2005, 43–44; 1995, 114–115; 126; 147; 166–67)</zotero>

XSLT:

<xsl:for-each select = "tokenize(zotero,';\s[^\d]')">
 <bibl><xsl:value-of select = "."/></bibl>
</xsl:for-each>

Текущий выход:

<bibl>(Leppin 2019</bibl>
<bibl>rancisco 2011, 119</bibl>
<bibl>an Ginkel 2005, 43–44; 1995, 114–115; 126; 147; 166–67)</bibl>

1
50
3

Ответы:

К сожалению, спецификация XPath не позволяет выполнять предварительный просмотр в регулярных выражениях.

Вы можете использовать обходной путь, например:

<xsl:variable name = "preprocess" select = "replace(zotero, ';\s(\D)', '&#133;$1')" />
<xsl:for-each select = "tokenize($preprocess, '&#133;')">
    <bibl>
        <xsl:value-of select = "."/>
    </bibl>
</xsl:for-each>

Или, если вы используете процессор Saxon, вы можете переключиться на стандартный механизм регулярных выражений Java и просто сделать:

<xsl:for-each select = "tokenize(zotero, ';\s(?=\D)', ';j')">

Решено

Я хочу написать токенизатор, который будет токенизировать только если точка с запятой без пробела и цифры

С отрицательным прогнозом, который будет выражаться как

  <xsl:template match = "zotero">
    <xsl:for-each select = "tokenize(., ';(?! [0-9])', ';j')">
      <bib>{.}</bib>
    </xsl:for-each>
  </xsl:template>

Флаги ;j работают с Saxon Java, SaxonC, Saxon .NET, SaxonCS и SaxonJS, позволяя переключаться со стандартных регулярных выражений XPath на поддерживаемую платформу.


Вместо использования функции tokenize я бы, вероятно, использовал xsl:analyze-string вот так:

<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
  <xsl:output method = "xml" indent = "true"/>

<xsl:template match = "zotero">
  <listBibl>
    <!-- each bibl starts with at least one letter \p{L} or space \p{Z},
    and continues with an optional sequence of non-letters \P{L} -->
    <xsl:analyze-string 
      select = "substring(., 2, string-length(.) - 2)" 
      regex = "(\p{{L}}|\p{{Z}})+\P{{L}}*"
    >
      <xsl:matching-substring>
        <bibl><xsl:value-of select = "."/></bibl>
      </xsl:matching-substring>
    </xsl:analyze-string>
  </listBibl>
</xsl:template>
 
</xsl:stylesheet>

Урожайность:

<listBibl>
   <bibl>Leppin 2019; </bibl>
   <bibl>Francisco 2011, 119; </bibl>
   <bibl>van Ginkel 2005, 43–44; 1995, 114–115; 126; 147; 166–67</bibl>
</listBibl>

Проблема с tokenize() в том, что он съедает ваши разделители, в отличие от <xsl:analyze-string> (или в XSLT 3 функции analyze-string()). Я думаю, что этот подход легче расширить, например. во вложенный набор операторов analyze-string, которые создают более детальную библиографическую разметку, например, элементы <date> и <citedRange>.