如何检查字符串是否是有效的XML元素名称?

Mik*_*rov 21 php regex xml

我需要PHP中的正则表达式或函数,它将验证字符串是一个很好的XML元素名称.

表格w3schools:

XML元素必须遵循以下命名规则:

  1. 名称可以包含字母,数字和其他字符
  2. 名称不能以数字或标点字符开头
  3. 名称不能以字母xml(或XML或Xml等)开头
  4. 名称不能包含空格

我可以编写一个基本的正则表达式来检查规则1,2和4,但它不会考虑所有允许的标点符号,也不会考虑第3条规则

\w[\w0-9-]
Run Code Online (Sandbox Code Playgroud)

友情更新

以下是格式良好的XML元素名称的权威来源:

名称和代币

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)*
Run Code Online (Sandbox Code Playgroud)

还指定了单独的非标记化规则:

以字符串"xml"开头的名称,或者与任何匹配的字符串(('X'|'x')('M'|'m')('L'|'l'))的名称保留用于标准化在本规范的此版本或未来版本中.

Gor*_*don 22

如果要创建有效的XML,请使用DOM扩展.这样你就不用担心任何正则表达式.如果您尝试为DomElement输入无效名称,则会收到错误消息.

function isValidXmlName($name)
{
    try {
        new DOMElement($name);
        return TRUE;
    } catch(DOMException $e) {
        return FALSE;
    }
}
Run Code Online (Sandbox Code Playgroud)

这会给

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
Run Code Online (Sandbox Code Playgroud)

并且很可能足以满足您的需求.

迂腐1

请注意localName和QName之间的区别.如果冒号前面有前缀,则ext/dom假设您正在使用命名空间元素,这会增加名称的形成方式.从技术上讲,b:b是一个有效的本地名称,因为NameStartChar是NameChar的一部分.如果要包含这些,请将功能更改为

function isValidXmlName($name)
{
    try {
        new DOMElement(
            $name,
            null,
            strpos($name, ':') >= 1 ? 'http://example.com' : null
        );
        return TRUE;
    } catch(DOMException $e) {
        return FALSE;
    }
}
Run Code Online (Sandbox Code Playgroud)

迂腐2

请注意,元素可以以"xml"开头.W3schools(不隶属于W3c)显然错误地认定了这一部分(这不是第一次).如果你真的想要排除以xml add开头的元素

if(stripos($name, 'xml') === 0) return false;
Run Code Online (Sandbox Code Playgroud)

之前try/catch.

  • @ xsaero00,首先:我们通常不会低估所有我们不接受的答案.给出的所有答案都包含解决问题的有效方法.其次,我已经对我的解决方案(包括strpos)与已接受的解决方案进行了基准测试,顺便说一句,我的解决方案速度提高了250%.如果您不相信,请自己做一个基准测试. (9认同)

hak*_*kre 15

到目前为止,这已经被遗漏了,尽管问题是旧的:通过PHP的pcre函数进行名称验证,这些函数通过XML规范进行了简化.

XML的定义非常清楚它的规范中的元素名称(可扩展标记语言(XML)1.0(第五版)):

[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)*
Run Code Online (Sandbox Code Playgroud)

这种表示法可以转换为UTF-8兼容的正则表达式preg_match,这里用作单引号的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'
Run Code Online (Sandbox Code Playgroud)

或者作为另一种具有更可读方式的命名子模式的变体:

'~
# 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'
Run Code Online (Sandbox Code Playgroud)

请注意,此模式包含:您可能要排除的冒号(第一个模式中的两个出现,第二个模式中的一个),用于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)
Run Code Online (Sandbox Code Playgroud)

元素名称以XML(以小写或大写字母开头)不可能的说法是不正确的.<XML/>是一个格式完美的XML,XML是一个结构完美的元素名称.

这仅仅是这种名称是在被形成的孔的元素名称的所述子集预留给标准化(XML版本1.0和以上).如果使用字符串比较保留(格式良好的)元素名称,则很容易测试:

$reserved = $valid && 0 === stripos($name, 'xml'));
Run Code Online (Sandbox Code Playgroud)

或者另一个正则表达式:

$reserved = $valid && 1 === preg_match('~^[Xx][Mm][Ll]~', $name);
Run Code Online (Sandbox Code Playgroud)

PHP的DOMDocument可以考,至少我不知道任何方式如何做到这一点,我一直在寻找很多保留名称.

有效的元素名称需要一个独特的元素类型声明,这似乎超出了问题的范围,因为没有提供此类声明.因此,答案并没有解决这个问题.如果存在元素类型声明,则只需要针对所有(区分大小写)名称的白名单进行验证,因此这将是一个简单区分大小写的字符串比较.


游览:DOMDocument正则表达式有什么不同?

DOMDocument/ 相比DOMElement,有一些差异限定了有效的元素名称.DOM扩展处于某种混合模式,这使得它无法预测它的验证.以下偏移说明了行为并显示了如何控制它.

让我们来$name实例化一个元素:

$element = new DOMElement($name);
Run Code Online (Sandbox Code Playgroud)

结果取决于:

所以第一个角色决定比较模式.

正则表达式专门写入要检查的内容,这里是XML 1.0 Name符号.

您可以DOMElement通过在冒号前加上名称来实现相同的目的:

function isValidXmlName($name)
{

    try {
        new DOMElement(":$name");
        return TRUE;
    } catch (DOMException $e) {
        return FALSE;
    }
}
Run Code Online (Sandbox Code Playgroud)

要明确检查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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Leo*_*Leo 8

怎么样

/\A(?!XML)[a-z][\w0-9-]*/i
Run Code Online (Sandbox Code Playgroud)

用法:

if (preg_match('/\A(?!XML)[a-z][\w0-9-]*/i', $subject)) {
    # valid name
} else {
    # invalid name
}
Run Code Online (Sandbox Code Playgroud)

说明:

\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
Run Code Online (Sandbox Code Playgroud)

  • 这与<äøñ>不匹配,它是从XML 1.1开始的有效Nmtoken.见http://www.w3.org/TR/xml11/#sec-common-syn (12认同)
  • 这也没提''.(句点/句号),在XML元素名称中也有效. (2认同)