正则表达式检查c#中的有效属性名称

时间:2015-11-14 17:44:29

标签: c# regex

我需要验证要检索的属性名称的用户输入。

例如,用户可以为Windows窗体控件对象键入" Parent.Container" 属性,或只输入" Name" 属性。然后我用反射来获得属性的价值。

我需要检查用户是否输入了 c#property 的合法符号(或者只是合法的单词符号,如\ w),并且此属性也可以是复合的(包含两个或多个用点分隔的单词) )。

我现在有这个,这是一个正确的解决方案吗?

^([\w]+\.)+[\w]+$|([\w]+)

我使用Regex.IsMatch方法,当我通过"?someproperty" 时,它返回true,但" \ w"不包括"?"

3 个答案:

答案 0 :(得分:2)

不是最好的,但这会奏效。演示here

^@?[a-zA-Z_]\w*(\.@?[a-zA-Z_]\w*)*$

请注意 *号码0-9 允许作为第一个字符
* @仅允许 作为第一个字符,但不允许其他任何地方(编译器会剥离) * _是允许的

修改

根据您的要求,下面的Regex会更有用,因为输入属性名称中不需要@。检查here

^[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*$

答案 1 :(得分:2)

我也一直在寻找这个,但是我不知道现有的答案是完整的。经过一番挖掘,这就是我发现的东西。

澄清我们想要的东西

首先,我们需要知道想要哪个有效:根据运行时间有效还是根据语言有效?例子:

  • Foo\u0123Bar是C#语言的有效属性名称,而不是 runtime 的有效属性名称。差异由编译器平滑处理,编译器将标识符安静地转换为FooģBar
  • 对于逐字标识符(@前缀),该语言将@视为标识符的一部分,但运行时看不到它。

根据您的需求,任何一种都可以。如果要将经过验证的文本输入到反射方法(例如GetProperty(string))中,则需要 runtime 有效版本。但是,如果您希望C#开发人员更熟悉的语法,则需要语言-有效版本。

基于运行时的“有效”

C#版本5是(截至7/2018)具有正式标准的最新版本:ECMA 334规范。它的规则是:

  

此子节中给出的标识符规则完全对应   除Unicode标准附件15建议的内容外,   下划线可以用作初始字符(如   C语言),Unicode转义序列允许在   标识符,并且允许使用“ @”字符作为前缀来启用   用作标识符的关键字。

提到的“ Unicode标准附件15”为Unicode TR 15, Annex 7,将基本模式形式化为:

<identifier> ::= <identifier_start> ( <identifier_start> | <identifier_extend> )*

<identifier_start> ::= [{Lu}{Ll}{Lt}{Lm}{Lo}{Nl}]

<identifier_extend> ::= [{Mn}{Mc}{Nd}{Pc}{Cf}]

{花括号中的代码}是Unicode类,它们直接通过\p{category}映射到Regex。因此(经过一些简化),根据 runtime 检查“有效”的基本正则表达式为:

@"^[\p{L}\p{Nl}_][\p{Cf}\p{L}\p{Mc}\p{Mn}\p{Nd}\p{Nl}\p{Pc}]*$"

所有丑陋的细节

C#规范还要求标识符采用Unicode规范化形式C。但是,不需要编译器实际执行它。至少Roslyn C#编译器允许使用非规范形式的标识符(例如E\u0304\u0306),并将其与等效的规范形式的标识符(例如\u0100\u0306)区别对待。而且,据我所知,没有一种用正则表达式来代表这样的规则的理智方法。如果您不需要/希望用户能够区分看起来完全相同的属性,我的建议是仅对用户输入运行string.Normalize()即可完成操作。

C#规范说,如果两个标识符仅在格式化字符方面有所不同,则它们是等效的。例如,Elmo(四个字符)和El­moEl\u00ADmo)是相同的标识符。 (请注意:这是软连字符,通常不可见;但是某些字体可能会显示出来。)如果不可见字符的存在会给您带来麻烦,则可以从正则表达式中删除\p{Cf}。这并不会减少您接受的标识符,而只是减少您接受的格式。

C#规范保留包含“ __”的标识符供自己使用。根据您的需要,您可能希望排除该可能性。这可能是与正则表达式分开的操作。

嵌套,泛型等

Reflection,Type,IL和其他地方有时会显示类名或带有额外符号的方法名。例如,类型名称可以指定为X`1+Y[T]。多余的东西不是标识符的 部分-这是表示类型信息的不相关方式。

基于语言的“有效”

这只是以前的正则表达式,还允许:

  • 前缀@
  • Unicode转义序列

第一个是微不足道的修改:只需添加@?

Unicode转义序列的格式为@"\\[Uu][\dA-Fa-f]{4}"。我们可能会尝试将它们楔入[ ... ]对中并称其为完成,但是这样做会错误地允许(例如)\u0000作为标识符。我们需要将转义序列限制为产生其他可接受字符的序列。一种方法是进行预转换以转换转义序列:将所有\\[Uu][\dA-Fa-f]{4}替换为相应的字符。

因此,将所有内容放在一起,从C#语言的角度检查字符串是否有效:

bool IsValidIdentifier(string input)
{
    if (input is null) { throw new ArgumentNullException(); }

    // Technically the input must be in normal form C. Implementations aren't required
    // to verify that though, so you could remove this check if your runtime doesn't
    // mind.
    if (!input.IsNormalized())
    {
        return false;
    }

    // Convert escape sequences to the characters they represent. The only allowed escape
    // sequences are of form \u0000 or \U0000, where 0 is a hex digit.
    MatchEvaluator replacer = (Match match) =>
        {
            string hex = match.Groups[1].Value;
            var codepoint = int.Parse(hex, NumberStyles.HexNumber);
            return new string((char)codepoint, 1);
        };
    var escapeSequencePattern = @"\\[Uu]([\dA-Fa-f]{4})";
    var withoutEscapes = Regex.Replace(input, escapeSequencePattern, replacer, RegexOptions.CultureInvariant);
    withoutEscapes.Dump();

    // Now do the real check.
    var isIdentifier = @"^@?[\p{L}\p{Nl}_][\p{Cf}\p{L}\p{Mc}\p{Mn}\p{Nd}\p{Nl}\p{Pc}]*$";
    return Regex.IsMatch(withoutEscapes, isIdentifier, RegexOptions.CultureInvariant);
}

回到原始问题

提问者早已不复存在,但我觉得有义务提供对实际问题的答案:

string[] parts = input.Split();
return parts.Length == 2
  && IsValidIdentifier(parts[0])
  && IsValidIdentifier(parts[1]);

来源

ECMA 334§7.4.3; ECMA 335§I.10; Unicode TR 15 Annex 7

答案 2 :(得分:0)

您在评论中发布的内容几乎是正确的。但它不会检测单个属性,例如&#34; Name&#34;。

^(?:[\w]+\.)*\w+$

按预期工作。刚刚将+更改为*,将组更改为非捕获组,因为您不关心这里的组。