如何检查string是否是有效的XML元素名称?
我需要在PHP中的正则expression式或函数,将validationstring是一个很好的XML元素名称。
表格w3schools:
XML元素必须遵循这些命名规则:
- 名称可以包含字母,数字和其他字符
- 名称不能以数字或标点符号开头
- 名称不能以字母xml(或XML或Xml等)开头,
- 名称不能包含空格
我可以写一个基本的正则expression式来检查规则1,2和4,但它不会考虑所有标点符号,也不会考虑第三条规则
\w[\w0-9-]
友好的更新
以下是格式正确的XML元素名称的更权威来源:
名称和标记
NameStartChar ::= ":" | [AZ] | "_" | [az] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] Name ::= NameStartChar (NameChar)*
还指定了一个单独的非标记规则:
名称以string“xml”开头,或与任何匹配(('X'|'x')('M'|'m')('L'|'l'))的string保留用于标准化在本规范的这个或者将来的版本中。
怎么样
/\A(?!XML)[az][\w0-9-]*/i
用法:
if (preg_match('/\A(?!XML)[az][\w0-9-]*/i', $subject)) { # valid name } else { # invalid name }
说明:
\A Beginning of the string (?!XML) Negative lookahead (assert that it is impossible to match "XML") [az] Match a non-digit, non-punctuation character [\w0-9-]* Match an arbitrary number of allowed characters /i make the whole thing case-insensitive
如果您想创build有效的XML ,请使用DOM扩展 。 这样你就不必担心任何正则expression式。 如果您尝试向DomElementinput无效的名称,则会出现错误。
function isValidXmlName($name) { try { new DOMElement($name); return TRUE; } catch(DOMException $e) { return FALSE; } }
这会给
var_dump( isValidXmlName('foo') ); // true valid localName var_dump( isValidXmlName(':foo') ); // true valid localName var_dump( isValidXmlName(':b:c') ); // true valid localName var_dump( isValidXmlName('b:c') ); // false assumes QName
而且对于你想做的事很可能已经足够了。
迂腐的笔记1
请注意localName和QName之间的区别。 ext / dom假定如果在冒号前有一个前缀,就使用了一个名称空间元素,这就增加了如何形成名字的限制。 从技术上来说,b:b是一个有效的本地名,尽pipe因为NameStartChar是NameChar的一部分 。 如果你想包括这些,改变function
function isValidXmlName($name) { try { new DOMElement( $name, null, strpos($name, ':') >= 1 ? 'http://example.com' : null ); return TRUE; } catch(DOMException $e) { return FALSE; } }
迂腐的笔记2
请注意,元素可能以“xml”开头。 W3schools(不属于W3c)显然弄错了这部分( 不会是第一次 )。 如果你真的想排除从XML添加开始的元素
if(stripos($name, 'xml') === 0) return false;
在try/catch
之前。
到目前为止,这个问题已经被遗漏了,尽pipe问题在于这个问题:通过PHP的pcre函数进行名称validation,这些函数是用XML规范简化的。
XML的定义非常清晰地描述了规范中的元素名称( 可扩展标记语言(XML)1.0(第五版) ):
[4] NameStartChar ::= ":" | [AZ] | "_" | [az] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] [5] Name ::= NameStartChar (NameChar)*
这个表示法可以转换成与preg_match
一起使用的UTF-8兼容正则expression式,在这里作为单引号的PHPstring被逐字复制:
'~^[:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}][:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]*$~u'
或者作为具有更具可读性的命名子模式的另一种变体:
'~ # XML 1.0 Name symbol PHP PCRE regex <http://www.w3.org/TR/REC-xml/#NT-Name> (?(DEFINE) (?<NameStartChar> [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) (?<NameChar> (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) (?<Name> (?&NameStartChar) (?&NameChar)*) ) ^(?&Name)$ ~ux'
请注意,此模式包含冒号:
您可能想要为XML名称空间validation原因(例如对NCName
的testing)排除(第一个模式中的两个副本,第二个副本中的两个副本)。
用法示例:
$name = '::...'; $pattern = '~ # XML 1.0 Name symbol PHP PCRE regex <http://www.w3.org/TR/REC-xml/#NT-Name> (?(DEFINE) (?<NameStartChar> [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) (?<NameChar> (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) (?<Name> (?&NameStartChar) (?&NameChar)*) ) ^(?&Name)$ ~ux'; $valid = 1 === preg_match($pattern, $name); # bool(true)
用XML
(大写或小写)开头的元素名不可能是不正确的。 <XML/>
是一个完美格式化的XML, XML
是一个非常完美的元素名称。
只是这些名字是为标准化而保留的格式良好的元素名称的子集(XML版本1.0及以上)。 很容易testing(格式良好的)元素名称是否与string比较保留:
$reserved = $valid && 0 === stripos($name, 'xml'));
或者另一个正则expression式:
$reserved = $valid && 1 === preg_match('~^[Xx][Mm][Ll]~', $name);
PHP的DOMDocument
不能testing保留名称,至less我不知道如何做到这一点,我一直在寻找很多东西。
有效的元素名称需要唯一的元素types声明 ,这似乎超出了这个问题的范围,因为没有提供这样的声明。 因此,答案并没有考虑到这一点。 如果会有元素types声明,则只需要对所有(区分大小写)的名称进行validation,所以这将是一个简单的区分大小写的string比较。
游览: DOMDocument
对正则expression式有什么不同?
与DOMDocument
/ DOMElement
相比,有效的元素名称有一些不同之处。 DOM扩展处于某种混合模式,这使得它不太可预测它的validation。 下面的游览说明了行为并展示了如何控制它。
让我们拿$name
并实例化一个元素:
$element = new DOMElement($name);
结果取决于:
- 如果第一个字符是冒号,则只validationXML 1.0
Name
符号 。 - 如果第一个字符不是冒号,则validationXMLNS 1.0
QName
符号
所以第一个字符决定比较模式。
一个正则expression式是专门写什么来检查的,这里是XML 1.0的Name
符号。
您可以通过在冒号前添加名称来达到与DOMElement
相同的效果:
function isValidXmlName($name) { try { new DOMElement(":$name"); return TRUE; } catch (DOMException $e) { return FALSE; } }
要显式检查QName
可以通过将其转换为PrefixedName
来防止它是PrefixedName
:
function isValidXmlnsQname($qname) { $prefixedName = (!strpos($qname, ':') ? 'prefix:' : '') . $qname; try { new DOMElement($prefixedName, NULL, 'uri:ns'); return TRUE; } catch (DOMException $e) { return FALSE; } }
受到mef好的回答的启发,但是以'$'结尾,否则将接受包含'aaa bbb'之类空格的XML名称。
$validXmlName = (preg_match('/^(?!XML)[az][\w0-9-]*$/i', $subject) != 0);
使用这个正则expression式:
?^ _((XML |?![_ \ d \ W]))([\ w .-] +)$
这匹配你所有的四个点,并允许unicode字符。
如果您正在使用DotNet框架,请尝试XmlConvert.VerifyName。 它会告诉你,如果名称是有效的,或使用XmlConvert.EncodeName实际上将一个无效的名称转换为有效的名称…
下面的expression式应该匹配除xml以外的有效unicode元素名称。 以xml开始或结束的名称仍将被允许。 这通过@ toscho'stesting。 我无法弄清楚正则expression式的一个原因是扩展。 XML元素名称规范说:
[4] NameChar :: = Letter | 数字| '' | ' – '| '_'| ':'| CombiningChar | 扩展
[5] Name :: =(Letter |'_'|':')(NameChar)*
但是,对于包含扩展器的unicode类别或类没有明确的定义。
^[\p{L}_:][\p{N}\p{L}\p{Mc}.\-|:]*((?<!xml)|xml)$
XML,xml等是有效的标签,它们只是“保留用于本规范的这个版本或未来版本的标准化”,可能永远不会发生。 请在https://www.w3.org/TR/REC-xml/查看真实的标准。; w3school的文章是不准确的。
这应该给你粗略的你需要[假设你使用Unicode]:
( 注意:这是完全未经testing的。)
[^\p{P}xX0-9][^mMlL\s]{2}[\w\p{P}0-9-]
\p{P}
是PHP正则expression式语法中Unicode标点符号的语法。
if (substr(strtolower($text), 0, 3) != 'xml') && (1 === preg_match('/^\w[^<>]+$/', $text))) { // valid; }