2013-07-11 4 views
2

Мой текущий проект вращается вокруг перевода нескольких тестовых примеров в документ в формате XML, совместимого с системой управления тестовыми случаями. Во многих из этих случаев заголовок имеет префикс с несколькими идентификаторами билетов, номерами документов и т. Д., Которые необходимо удалить, прежде чем они могут быть загружены в систему.XSLT - анализ регулярных выражений

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

Пример ввода

1.

<case-name>3.1.6 (C0) TID#EIIY CHM-2213 BZ-7043 Client side Java Upgrade R8</case-name> 

2.

<case-name>4.2.7 (C1) TID#F1DR – AIP - EHD-319087 - BZ6862 - Datalink builder res...</case-name> 

Желаемая Выход

1.

<tr:summary>Client side Java Upgrade R8</tr:summary> 

2.

<tr:summary>Datalink builder res...</tr:summary> 

Первый подход

<xsl:template match="case-name"> 
    <tr:summary> 
     <xsl:variable name="start"> 
      <xsl:apply-templates/> 
     </xsl:variable> 
     <xsl:variable name="start" select="normalize-space($start)"/> 
     <xsl:variable name="noFloat"  select="normalize-space(fn:remFirstRegEx($start,  '^[0-9]+([.][0-9]+)*'      ))"/> 
     <xsl:variable name="noFloatDash" select="normalize-space(fn:remFirstRegEx($noFloat,  '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noC"   select="normalize-space(fn:remFirstRegEx($noFloatDash, '^\(C[0-2]\)'        ))"/> 
     <xsl:variable name="noCDash"  select="normalize-space(fn:remFirstRegEx($noC,   '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noTID"   select="normalize-space(fn:remFirstRegEx($noCDash,  '^(TID)(#|\p{Pd})(\w+)'     ))"/> 
     <xsl:variable name="noTIDDash"  select="normalize-space(fn:remFirstRegEx($noTID,  '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noAIP"   select="normalize-space(fn:remFirstRegEx($noTIDDash, '^AIP'          ))"/> 
     <xsl:variable name="noAIPDash"  select="normalize-space(fn:remFirstRegEx($noAIP,  '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noCHM"   select="normalize-space(fn:remFirstRegEx($noAIPDash, '^(CHM)[\p{Pd}]([0-9]+)'     ))"/> 
     <xsl:variable name="noCHMDash"  select="normalize-space(fn:remFirstRegEx($noCHM,  '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noEHD"   select="normalize-space(fn:remFirstRegEx($noCHMDash, '^(EHD)[\p{Pd}]([0-9]+)'     ))"/> 
     <xsl:variable name="noEHDDash"  select="normalize-space(fn:remFirstRegEx($noEHD,  '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noBZ"   select="normalize-space(fn:remFirstRegEx($noEHDDash, '^(BZ)(((#|\p{Pd})[0-9]+)|[0-9]+)'   ))"/> 
     <xsl:variable name="noBZDash"  select="normalize-space(fn:remFirstRegEx($noBZ,   '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="noTT"   select="normalize-space(fn:remFirstRegEx($noBZDash,  '^(TT)[#](\w)+'       ))"/> 
     <xsl:variable name="noTTDash"  select="normalize-space(fn:remFirstRegEx($noTT,   '^[\p{Pd}]'        ))"/> 
     <xsl:variable name="nobrack"  select="normalize-space(fn:remFirstRegEx($noTTDash,  '^\[(.*?)\]'        ))"/> 
     <xsl:variable name="noBrackDash" select="normalize-space(fn:remFirstRegEx($nobrack,  '^[\p{Pd}]'        ))"/> 
     <xsl:value-of select="normalize-space($noBrackDash)"/> 
    </tr:summary> 
</xsl:template> 

<xsl:function name="fn:remFirstRegEx"> 
    <xsl:param name="inString"/> 
    <xsl:param name="regex"/> 

    <xsl:variable name="words" select="tokenize($inString, '\p{Z}')"/> 
    <xsl:variable name="outString"> 
     <xsl:for-each select="$words"> 
      <xsl:if test="not(matches(., $regex)) or index-of($words, .) > 1"> 
       <xsl:value-of select="."/><xsl:text> </xsl:text> 
      </xsl:if> 
     </xsl:for-each> 
    </xsl:variable> 

    <xsl:value-of select="string-join($outString, '')"> 
</xsl:function> 

Примечание: Пространство имен сноска, для целей этого перевода, является просто «функцией/пространством имен», используемым для написания моих собственных функций.

Первые результаты

1. Успех

<tr:summary>Client side Java Upgrade R8</tr:summary> 

2. Отказ

<tr:summary>- EHD-319087 - BZ6862 - Datalink builder resolution selector may drop leading zeros on coordinate seconds</tr:summary> 

Второй подход

<xsl:function name="fn:remFirstRegEx"> 
    <xsl:param name="inString"/> 
    <xsl:param name="regex"/> 

    <xsl:analyze-string select="$inString" regex="$regex"> 
     <xsl:non-matching-substring> 
      <xsl:value-of select="."/> 
     </xsl:non-matching-substring> 
    </xsl:analyze-string> 
</xsl:function> 

Этот подход полностью не подходит, я включаю его здесь, потому что это более очевидное решение и не работает вообще.

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

Проблема, как я уже сказал, связана с тире. Я отметил, что в каждом случае в документах, в которых перевод не удался, идентификатору сбоя предшествовали и, а затем тире.Если это только предшествует, оно пройдет отлично. Если это только следует, проблем нет. Оба - это то место, где он падает, и, как ни странно, черта все еще появляется, хотя она уже, казалось бы, была исключена из строки.

Здесь есть два вида тире: обычная тире (&#8211;) и знак минуса (&#45;).

Парадоксально: извините за длинный вопрос, и дайте мне знать, если я что-то пропустил.

EDIT: Забыл сказать, все регулярные выражения, за исключением тире были испытаны в другом месте и, как известно, работают на всех входных вещи.

EDIT II: После решения @ acheong87, я попытался запустить следующее:

<xsl:template match="case-name"> 
     <tr:summary> 
     <xsl:variable name="regEx" select= 
     "'^[\s\p{Pd}]*(\d+([.]\d+)*)?[\s\p{Pd}]*(\(C[0-2]\))?([\s\p{Pd}]*(TID|AIP|CHM|EHD|BZ|TT)((#|\p{Pd}|)\w+|))*[\s\p{Pd}]*(\[.*?\])?'"/> 
     <xsl:analyze-string select="string(.)" regex="{$regEx}"> 
      <xsl:non-matching-substring> 
       <xsl:value-of select="."/> 
      </xsl:non-matching-substring> 
     </xsl:analyze-string> 
    </tr:summary> 
</xsl:template> 

И Saxon дает мне следующую ошибку:

Error at xsl:analyze-string at line (for our purposes, 5): 
XTDE1150: The regular expression must not be one that matches a zero-length string 

я могу получить, почему это придет при условии, что все не является обязательным. Есть ли другой способ его запуска, который не даст мне эту ошибку?

Еще раз спасибо.

+0

Итак, что вы на самом деле ищете, достаточно разумное регулярное выражение, чтобы иметь дело с вашим (надеюсь, ограниченным) количеством форматов входных строк? – Tomalak

+0

@ acheong87 Регулярное выражение 'находится в конце строк в шаблоне первого решения, второе решение использует тот же шаблон. –

+0

@ Томалак Я не уверен, если это возможно, если честно. Полный список регулярных выражений есть, если вы можете найти регулярное выражение, чтобы решить их все, не зачерпывая что-либо еще, кроме того, что (а) вы являетесь мастером, и (b) это будет оценено по достоинству. –

ответ

2

Вот основные компоненты, которые попадают в одно регулярное выражение. Я переписал некоторые из ваших выражений.

\d+([.]\d+)* 
\(C[0-2]\) 
TID(#|\p{Pd})\w+ 
AIP 
CHM[\p{Pd}]\d+ 
EHD[\p{Pd}]\d+ 
BZ(#|\p{Pd}|)\d+ 
TT#\w+ 
\[.*?\] 

Каждый компонент должен быть завернут в (...)?, чтобы сделать его необязательным, и все компоненты должны быть соединены с помощью сепаратора, [\s\p{Pd}]*. Это дает:

^[\s\p{Pd}]*(\d+([.]\d+)*)?[\s\p{Pd}]*(\(C[0-2]\))?[\s\p{Pd}]*(TID(#|\p{Pd})\w+)?[\s\p{Pd}]*(AIP)?[\s\p{Pd}]*(CHM[\p{Pd}]\d+)?[\s\p{Pd}]*(EHD[\p{Pd}]\d+)?[\s\p{Pd}]*(BZ(#|\p{Pd}|)\d+)?[\s\p{Pd}]*(TT#\w+)?[\s\p{Pd}]*(\[.*?\])? 

Вы можете увидеть в this Rubular demo, что приведенное выше выражение действительно соответствует вашим два примера.


Там может быть элегантным упрощением вы можете быть заинтересованы в.

\d+([.]\d+)* 
\(C[0-2]\) 
(TID|AIP|CHM|EHD|BZ|TT)((#|\p{Pd}|)\w+|) 
\[.*?\] 

Может быть, некоторые коды, такие как AIP должны быть разделены, но вы можете увидеть дух этой версии. То есть маловероятно, что действительные названия начнутся с таких кодов; на самом деле, вероятно, более вероятно, что вашими примерами может быть отсутствует возможная комбинация, такая как EHD#, которая может появиться в будущем, но ваша устаревшая формулировка пропустит. (Конечно, моя точка зрения не имеет значения, если - это нет будущего —, и данные, которые у вас есть, - это единственные данные, которые вам нужно обработать.) Если есть будущее, IMO, лучше в этом случае ослабить строгости выражения для захвата потенциальных связанных комбинаций.

выше станет:

^[\s\p{Pd}]*(\d+([.]\d+)*)?[\s\p{Pd}]*(\(C[0-2]\))?([\s\p{Pd}]*(TID|AIP|CHM|EHD|BZ|TT)((#|\p{Pd}|)\w+|))*[\s\p{Pd}]*(\[.*?\])? 

Вот the Rubular demo.

+0

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

+0

@ DanMcElroy - Добавлены якоря (что сделало демоверсии Rubular лучше выглядеть). Кроме того, в исходном решении удалены ненужные парсеры. Наконец, отредактировано в более общем выражении, которое может быть более надежным (и более простым для расширения). –

+0

Супер полезно. Дайте мне лучшее представление о регулярных выражениях в процессе (вы могли бы сказать из оригинальных выражений, что я еще не очень уверен в них). Еще раз спасибо! –

0

Одно регулярное выражение, чтобы управлять ими все выглядит

 
^      # start of string 
([0-9]\.[0-9.]+).*? # digits and dots 
\((C[0-2])\).*?  # C0, C1, C2 
((TID#\S+).*?)?  # TID... 
((AIP).*?)?   # AIP... 
((CHM\S+).*?)?  # CHM... 
((EHD\S+).*?)?  # EHD... 
((BZ\S+).*?)?   # BZ... 
(\w.*)?    # free text 
$      # end of string 
 
^([0-9]\.[0-9.]+).*?\((C[0-2])\).*?((TID#\S+).*?)?((AIP).*?)?((CHM\S+).*?)?((EHD\S+).*?)?((BZ\S+).*?)?(\w.*)?$ 

http://rubular.com/r/pPxKBVwJaE

.*? едят любой разделитель, пока не начнется следующий матч. Большинство совпадений являются необязательными, возможно даже больше, чем вам нужно быть дополнительным. Удалите прилагаемый номер (...)? для любого соответствия, которое вы хотите сделать обязательным. Необязательные группы подсчитываются, но могут быть пустыми.

Собирает все вместе

<xsl:variable name="linePattern">   <!-- group|contents  --> 
    <xsl:text>^</xsl:text>     <!--  start of string --> 
    <xsl:text>([0-9]\.[0-9.]+).*?</xsl:text> <!--  1 digits and dots --> 
    <xsl:text>\((C[0-2])\).*?</xsl:text>  <!--  2 C0, C1, C2  --> 
    <xsl:text>((TID#\S+).*?)?</xsl:text>  <!-- 3, 4 TID...   --> 
    <xsl:text>((AIP).*?)?</xsl:text>   <!-- 5, 6 AIP...   --> 
    <xsl:text>((CHM\S+).*?)?</xsl:text>  <!-- 7, 8 CHM...   --> 
    <xsl:text>((EHD\S+).*?)?</xsl:text>  <!-- 9, 10 EHD...   --> 
    <xsl:text>((BZ\S+).*?)?</xsl:text>  <!-- 11, 12 BZ...   --> 
    <xsl:text>(\w.*)?</xsl:text>    <!--  13 free text  --> 
    <xsl:text>$</xsl:text>     <!--  end of string --> 
</xsl:variable> 

<xsl:template match="case-name"> 
    <xsl:analyze-string select="string(.)" regex="{$linePattern}"> 
    <xsl:matching-substring> 
     <tr:summary> 
     <part><xsl:value-of select="regex-group(1)" /></part> 
     <part><xsl:value-of select="regex-group(2)" /></part> 
     <part><xsl:value-of select="regex-group(4)" /></part> 
     <part><xsl:value-of select="regex-group(6)" /></part> 
     <part><xsl:value-of select="regex-group(8)" /></part> 
     <part><xsl:value-of select="regex-group(10)" /></part> 
     <part><xsl:value-of select="regex-group(12)" /></part> 
     <part><xsl:value-of select="regex-group(13)" /></part> 
     </tr:summary> 
    </xsl:matching-substring> 
    <!-- 
     possibly include <xsl:non-matching-substring>, <xsl:fallback> 
    --> 
    </xsl:analyze-string> 
</xsl:template> 

Конечно, вы можете иметь дело с отдельными группами матча так, как вам нравится.

+0

Сначала пошел с другим решением, и это сработало, поэтому я ненавижу его смеяться, но это кажется совершенно законным. Пока не использовали группы регулярных выражений, поэтому я буду учитывать этот ответ, если возникнет такая необходимость. Благодаря! –

+0

Я просто заметил, что XSLT 2.0 не поддерживает привязки к границам слов ('\ b') и не захватывающие группы (' (?: ...) '), поэтому мне пришлось немного изменить выражение и количество групп поднялся. Теперь все работает так, как ожидалось. – Tomalak

Смежные вопросы