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

Vas*_*liy 5 c# regex

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

例如,用户可以为 Windows 窗体控件对象键入“Parent.Container”属性或仅键入“Name”属性。然后我使用反射来获取属性的值。

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

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

^([\w]+\.)+[\w]+$|([\w]+)
Run Code Online (Sandbox Code Playgroud)

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

Emd*_*dot 6

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

明确我们想要什么

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

  • Foo\u0123Bar是 C#语言的有效属性名称,但不适用于运行时。编译器会消除差异,编译器会悄悄地将标识符转换为Foo?Bar.
  • 对于逐字标识符(@前缀),语言将@视为标识符的一部分,但运行时看不到它。

根据您的需要,两者都可能有意义。如果您将经过验证的文本提供给反射方法,例如GetProperty(string),您将需要运行时有效版本。但是,如果您想要 C# 开发人员更熟悉的语法,则需要语言- 有效版本。

基于运行时的“有效”

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

本小节中给出的标识符规则与 Unicode 标准附件 15 推荐的规则完全对应,除了允许下划线作为初始字符(在 C 编程语言中是传统的),标识符中允许使用 Unicode 转义序列,以及“ @” 字符被允许作为前缀,使关键字能够用作标识符。

提到的“Unicode Standard Annex 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}]
Run Code Online (Sandbox Code Playgroud)

{大括号中的代码} 是 Unicode 类,它们通过\p{category}. 因此(稍微简化后)根据运行时检查“有效”的基本正则表达式将是:

@"^[\p{L}\p{Nl}_][\p{Cf}\p{L}\p{Mc}\p{Mn}\p{Nd}\p{Nl}\p{Pc}]*$"
Run Code Online (Sandbox Code Playgroud)

所有丑陋的细节

C# 规范还要求标识符采用 Unicode 规范化形式 C。不过,它并不要求编译器实际执行它。至少 Roslyn C# 编译器允许非范式标识符(例如,E\u0304\u0306)并将它们视为与等效的范式标识符(例如\u0100\u0306)不同。无论如何,据我所知,没有理智的方式用正则表达式来表示这样的规则。如果您不需要/希望用户能够区分看起来完全相同的属性,我的建议是只运行string.Normalize()用户的输入来完成它。

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

C# 规范保留包含“__”的标识符供自己使用。根据您的需要,您可能希望排除它。这应该是一个独立于正则表达式的操作。

嵌套、泛型等。

反射、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);
}
Run Code Online (Sandbox Code Playgroud)

回到最初的问题

提问者早已不在,但我觉得有必要回答实际问题:

string[] parts = input.Split();
return parts.Length == 2
  && IsValidIdentifier(parts[0])
  && IsValidIdentifier(parts[1]);
Run Code Online (Sandbox Code Playgroud)

来源

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


Arg*_*a C 5

不是最好的,但这会起作用。演示在这里

^@?[a-zA-Z_]\w*(\.@?[a-zA-Z_]\w*)*$
Run Code Online (Sandbox Code Playgroud)

请注意,
* 不允许将数字0-9作为一个字符
*@允许作为第一个字符,但不允许作为其他任何地方(编译器将删除) *是允许的
_

编辑

根据您的要求,下面的内容Regex会更有用,因为输入属性名称不需要包含@在其中。检查这里

^[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*$
Run Code Online (Sandbox Code Playgroud)