XSLT - 正则表达式解析

时间:2013-07-11 14:35:49

标签: regex xslt-2.0

我目前的项目围绕将文档中的大量测试用例转换为与测试用例管理系统兼容的XML形式。在许多这种情况下,标题的前缀是许多票证标识符,文档位置编号等,在将它们上传到系统之前需要将其删除。

鉴于许多这些票证标识符可能存在于标题的其他地方并且完全有效,我已经以当前形式编写了翻译,以便仅检查字符串的开头是否为正则表达式。我写了两种方法,结果各不相同。

示例输入

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>

注意:为了这个翻译的目的,名称空间fn只是&#34; function / namespace&#34;,用于编写我自己的函数。

第一个结果

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>

这种方法完全失败了,我把它包括在这里,因为它是更明显的解决方案,根本不起作用。

应该注意的是,上述解决方案中存在大量正则表达式,这是为了考虑可能出现的所有可能的ID。幸运的是,ID似乎按照一致的顺序排列。

正如我所得出的那样,问题在于破折号。我已经注意到,在翻译失败的文档的每个案例中,失败的ID都先于,然后是破折号。如果它只是在它之前,它会经历好的。如果只是如此,没有问题。 两者都是它落下的地方,奇怪的是,破折号仍然显示出来,即使它已经从字符串中看似已经消失了。

这里有两种破折号,正常破折号(&#8211;)和减号(&#45;)。

矛盾的是:对于这个长期的问题感到抱歉,如果我错过了什么,请告诉我。

编辑:忘记说,除了破折号之外的所有正则表达式都已经在别处测试过,并且已知可以处理所有输入的东西

编辑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>

撒克逊给了我以下错误:

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

我可以理解为什么会出现这种情况,因为一切都是可选的。是否有另一种运行它的方式,不会给我这个错误?

再次感谢。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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>

当然,您可以按照自己喜欢的方式处理各个匹配组。