首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何检查字符串是否是有效的XML元素名称?

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

Stack Overflow用户
提问于 2010-03-25 22:20:57
回答 9查看 33.9K关注 0票数 22

我需要PHP中的regex或函数来验证字符串是一个好的XML元素名称。

表格w3schools:

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

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

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

代码语言:javascript
运行
复制
\w[\w0-9-]

友好更新

这里是格式良好的XML元素名称的更权威的来源

名称和标记

代码语言:javascript
运行
复制
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‘’))开头的名称,都保留在本规范或未来版本的规范中用于标准化。

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2010-03-25 22:36:00

怎么样

代码语言:javascript
运行
复制
/\A(?!XML)[a-z][\w0-9-]*/i

用法:

代码语言:javascript
运行
复制
if (preg_match('/\A(?!XML)[a-z][\w0-9-]*/i', $subject)) {
    # valid name
} else {
    # invalid name
}

解释:

代码语言:javascript
运行
复制
\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
票数 8
EN

Stack Overflow用户

发布于 2010-03-25 22:36:10

如果要创建有效XML,请使用DOM扩展。这样你就不用担心任何Regex了。如果您试图将一个无效的名称输入到一个DomElement中,您将得到一个错误。

代码语言:javascript
运行
复制
function isValidXmlName($name)
{
    try {
        new DOMElement($name);
        return TRUE;
    } catch(DOMException $e) {
        return FALSE;
    }
}

这会给

代码语言:javascript
运行
复制
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是一个有效的本地名称,因为NameStartChar是NameChar的一部分。如果要包含这些内容,请将该函数更改为

代码语言:javascript
运行
复制
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开头的元素,请添加

代码语言:javascript
运行
复制
if(stripos($name, 'xml') === 0) return false;

try/catch之前。

票数 23
EN

Stack Overflow用户

发布于 2013-03-03 17:58:55

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

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

代码语言:javascript
运行
复制
[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字符串逐字复制:

代码语言:javascript
运行
复制
'~^[: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'

或者作为另一个以更易读的方式命名子模式的变体:

代码语言:javascript
运行
复制
'~
# 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的测试)。

用法示例:

代码语言:javascript
运行
复制
$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是一个格式非常好的元素名称。

只是这些名称位于格式良好的元素名称的子集中,这些元素名称是为标准化而保留的(XMLVersion1.0和更高版本)。很容易测试(格式良好的)元素名称是否通过字符串比较保留:

代码语言:javascript
运行
复制
$reserved = $valid && 0 === stripos($name, 'xml'));

或者另一个正则表达式:

代码语言:javascript
运行
复制
$reserved = $valid && 1 === preg_match('~^[Xx][Mm][Ll]~', $name);

DOMDocument不能测试保留的名称,至少我不知道怎么做,我一直在寻找。

一个有效的元素名称需要一个唯一的元素类型声明,这个声明似乎不在这里讨论的范围之内,因为没有提供这样的声明。因此,答案并没有考虑到这一点。如果有一个元素类型声明,您只需要根据所有(区分大小写的)名称的白列表进行验证,因此这将是一个简单的区分大小写的字符串比较。

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

DOMDocument / DOMElement相比,对有效元素名的限定有一些不同之处。DOM扩展处于某种混合模式,这使得它无法预测它验证的内容。下面的偏移说明了这种行为,并说明了如何控制它。

让我们接受$name并实例化一个元素:

代码语言:javascript
运行
复制
$element = new DOMElement($name);

结果取决于:

  • 如果第一个字符是冒号,它只验证符号
  • 如果第一个字符不是冒号,它将验证符号

因此,第一个字符决定比较模式。

正则表达式专门编写了要检查的内容,这里是XML1.0 Name符号。

您可以通过用冒号前缀名称来实现DOMElement的相同目标:

代码语言:javascript
运行
复制
function isValidXmlName($name)
{

    try {
        new DOMElement(":$name");
        return TRUE;
    } catch (DOMException $e) {
        return FALSE;
    }
}

要显式检查QName,可以通过将其转换为PrefixedName来实现,以防它是UnprefixedName

代码语言:javascript
运行
复制
function isValidXmlnsQname($qname)
{
    $prefixedName = (!strpos($qname, ':') ? 'prefix:' : '') . $qname;

    try {
        new DOMElement($prefixedName, NULL, 'uri:ns');
        return TRUE;
    } catch (DOMException $e) {
        return FALSE;
    }
}
票数 18
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2519845

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档