正则表达式正常工作,但在放置在XML模式中时失败

时间:2010-05-10 20:54:56

标签: python regex validation schema lxml

我有一个简单的doc.xml文件,其中包含一个带有Timestamp属性的单个根元素:

<?xml version="1.0" encoding="utf-8"?>
<root Timestamp="04-21-2010 16:00:19.000" />

我想针对我的简单schema.xsd验证此文档,以确保时间戳格式正确:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="root">
    <xs:complexType>
      <xs:attribute name="Timestamp" use="required" type="timeStampType"/>
    </xs:complexType>
  </xs:element>
  <xs:simpleType name="timeStampType">
    <xs:restriction base="xs:string">
      <xs:pattern value="(0[0-9]{1})|(1[0-2]{1})-(3[0-1]{1}|[0-2]{1}[0-9]{1})-[2-9]{1}[0-9]{3} ([0-1]{1}[0-9]{1}|2[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}.[0-9]{3}" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

所以我使用lxml Python模块并尝试执行简单的模式验证并报告任何错误:

from lxml import etree

schema = etree.XMLSchema( etree.parse("schema.xsd") )
doc = etree.parse("doc.xml")

if not schema.validate(doc):
    for e in schema.error_log:
        print e.message

我的XML文档验证失败,并显示以下错误消息:

Element 'root', attribute 'Timestamp': [facet 'pattern'] The value '04-21-2010 16:00:19.000' is not accepted by the pattern '(0[0-9]{1})|(1[0-2]{1})-(3[0-1]{1}|[0-2]{1}[0-9]{1})-[2-9]{1}[0-9]{3} ([0-1]{1}[0-9]{1}|2[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}.[0-9]{3}'.
Element 'root', attribute 'Timestamp': '04-21-2010 16:00:19.000' is not a valid value of the atomic type 'timeStampType'.

所以看起来我的正则表达式必定是错误的。但是当我尝试在命令行验证正则表达式时,它会传递:

>>> import re
>>> pat = '(0[0-9]{1})|(1[0-2]{1})-(3[0-1]{1}|[0-2]{1}[0-9]{1})-[2-9]{1}[0-9]{3} ([0-1]{1}[0-9]{1}|2[0-3]{1}):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}.[0-9]{3}'
>>> assert re.match(pat, '04-21-2010 16:00:19.000')
>>> 

我知道XSD正则表达式没有所有功能,但是the documentation I've found表示我正在使用的每个功能都应该有效。

那我错误理解的是什么,为什么我的文件失败了呢?

2 个答案:

答案 0 :(得分:3)

您的|匹配比您想象的要宽。

(0[0-9]{1})|(1[0-2]{1})-(3[0-1]{1}|[0-2]{1}[0-9]{1})-[2-9]{1}[0-9]{3}

被解析为:

(0[0-9]{1})
    -or-
(1[0-2]{1})-(3[0-1]{1}|[0-2]{1}[0-9]{1})-[2-9]{1}[0-9]{3}

如果你想避免它,你需要使用更多的分组; e.g。

((0[0-9]{1})|(1[0-2]{1}))-((3[0-1]{1}|[0-2]{1}[0-9]{1}))-[2-9]{1}[0-9]{3} (([0-1]{1}[0-9]{1}|2[0-3]{1})):[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}.[0-9]{3}

答案 1 :(得分:3)

表达式有几个错误。

  1. 您允许00作为有效月份。
  2. A|BC匹配ABC - 不是ACBC。因此,以(0[0-9]{1})|开头的表达式匹配包含0009的任何字符串。您想要的是(0[1-9]|1[0-2])-仅匹配0112后跟短划线。
  3. 您允许00作为有效日期。
  4. 该模式未锚定到文本的开头和结尾 - 添加^$。这就是使用Python的测试成功的原因。
  5. 顺便说一下 - 为什么不使用xs:dateTime?它的格式非常相似 - 我认为yyyy-mm-ddThh:mm:ss.fff