我需要一个PHP中的正则表达式或函数来验证字符串是一个很好的XML元素名称。
表格w3schools:
XML元素必须遵循这些命名 规则:
- 名称可以包含字母,数字和其他字符
- 名称不能以数字或标点符号开头
- 名称不能以字母xml(或XML,或Xml等)
开头- 名称不能包含空格
醇>
我可以编写一个基本的正则表达式来检查规则1,2和4,但它不会考虑所有允许的标点符号,也不会考虑第3条规则
\w[\w0-9-]
以下是well-formed XML Element names的权威来源:
名称和代币
NameStartChar ::=
":" | [A-Z] | "_" | [a-z] | [#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)*
还指定了单独的非标记化规则:
以字符串“xml”开头的名称,或任何匹配的字符串((''X'|'x')('M'|'m')('L'|'l')),是保留用于本规范的此版本或未来版本中的标准化。
答案 0 :(得分:20)
如果您想创建valid XML,请使用DOM Extension。这样你就不用担心任何正则表达式。如果您尝试为DomElement输入无效名称,则会收到错误。
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
并且可能对你想做的事情足够好。
请注意localName和QName之间的区别。如果冒号前面有前缀,则ext / dom假设您正在使用命名空间元素,这会增加名称的形成方式。从技术上讲,b:b是一个有效的本地名称,因为NameStartChar is part of NameChar。如果要包含这些,请将功能更改为
function isValidXmlName($name)
{
try {
new DOMElement(
$name,
null,
strpos($name, ':') >= 1 ? 'http://example.com' : null
);
return TRUE;
} catch(DOMException $e) {
return FALSE;
}
}
请注意,元素可能以“xml”开头。 W3schools(与W3c没有关系)显然错误地认定了这一部分(wouldn't be the first time)。如果你真的想要排除以xml add
开头的元素if(stripos($name, 'xml') === 0) return false;
在try/catch
。
答案 1 :(得分:14)
到目前为止,这已经被遗漏了,尽管事实上问题是旧的:通过PHP的pcre函数进行名称验证,这些函数通过XML规范进行了简化。
XML的定义非常清楚它的规格中的元素名称(Extensible Markup Language (XML) 1.0 (Fifth Edition)):
[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#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兼容正则表达式,这里作为单引号PHP字符串逐字复制:
'~^[: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名称空间验证原因(例如NCName
的测试)
用法示例:
$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及更高版本)保留的格式良好的元素名称的子集中。如果使用字符串比较保留(格式良好的)元素名称,则很容易测试:
$reserved = $valid && 0 === stripos($name, 'xml'));
或另一种正则表达式:
$reserved = $valid && 1 === preg_match('~^[Xx][Mm][Ll]~', $name);
PHP's DOMDocument
可以不测试保留名称,至少我不知道如何做到这一点,我一直在寻找。
有效的元素名称需要唯一元素类型声明,这似乎超出了问题的范围,因为没有提供此类声明。因此,答案并没有解决这个问题。如果存在元素类型声明,则只需要针对所有(区分大小写)名称的白名单进行验证,因此这将是一个简单区分大小写的字符串比较。
游览:DOMDocument
与正则表达式有什么不同?
与DOMDocument
/ DOMElement
相比,有些差异限定了有效的元素名称。 DOM扩展处于某种混合模式,这使得它无法预测它的验证。以下游览说明了行为,并展示了如何控制它。
让我们取$name
并实例化一个元素:
$element = new DOMElement($name);
结果取决于:
Name
symbol。QName
symbol 所以第一个角色决定比较模式。
正则表达式专门写入要检查的内容,这里是XML 1.0 Name
符号。
您可以通过在名称前添加冒号来为DOMElement
实现相同的目的:
function isValidXmlName($name)
{
try {
new DOMElement(":$name");
return TRUE;
} catch (DOMException $e) {
return FALSE;
}
}
要明确检查QName
这可以通过将PrefixedName
变为UnprefixedName
来实现,只要它是function isValidXmlnsQname($qname)
{
$prefixedName = (!strpos($qname, ':') ? 'prefix:' : '') . $qname;
try {
new DOMElement($prefixedName, NULL, 'uri:ns');
return TRUE;
} catch (DOMException $e) {
return FALSE;
}
}
:
{{1}}
答案 2 :(得分:8)
怎么样
/\A(?!XML)[a-z][\w0-9-]*/i
用法:
if (preg_match('/\A(?!XML)[a-z][\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")
[a-z] Match a non-digit, non-punctuation character
[\w0-9-]* Match an arbitrary number of allowed characters
/i make the whole thing case-insensitive
答案 3 :(得分:1)
受到mef好答案的启发,但结尾为'$'(否则将接受包含'aaa bbb'等空格的XML名称)
$validXmlName = (preg_match('/^(?!XML)[a-z][\w0-9-]*$/i', $subject) != 0);
答案 4 :(得分:0)
使用此正则表达式:
<强> ^ _((XML |?![_ \ d \ W]))([\瓦特.-] +)$ 强>
这匹配所有四个点,并允许使用unicode字符。
答案 5 :(得分:0)
如果您使用的是DotNet框架,请尝试使用XmlConvert.VerifyName。它会告诉您名称是否有效,或者使用XmlConvert.EncodeName将无效名称实际转换为有效名称...
答案 6 :(得分:0)
下面的表达式应匹配除xml之外的有效unicode元素名称。仍然允许以xml开头或结尾的名称。这通过了@ toscho的äøñ测试。我无法弄清楚正则表达式的一件事是扩展器。 xml元素名称规范说:
[4] NameChar :: = Letter |数字| '' | ' - '| '_'| ':'| CombiningChar |扩展
[5]姓名:: =(字母|'_'|':')(NameChar)*
但是对于包含扩展器的unicode类别或类没有明确的定义。
^[\p{L}_:][\p{N}\p{L}\p{Mc}.\-|:]*((?<!xml)|xml)$
答案 7 :(得分:0)
XML,xml等是有效的标签,它们只是“保留用于本规范的此版本或未来版本中的标准化”,这可能永远不会发生。请查看https://www.w3.org/TR/REC-xml/的真实标准。 w3school的文章不准确。
答案 8 :(得分:-1)
这应该大致给出你需要的东西[假设你正在使用Unicode]:
(注意:这是完全未经测试的。)
[^\p{P}xX0-9][^mMlL\s]{2}[\w\p{P}0-9-]
\p{P}
是PHP正则表达式语法中Unicode Punctuation marks的语法。
答案 9 :(得分:-3)
if (substr(strtolower($text), 0, 3) != 'xml') && (1 === preg_match('/^\w[^<>]+$/', $text)))
{
// valid;
}