Подтвердить что ты не робот

Есть ли способ на С#, чтобы проверить, является ли строка допустимым идентификатором

В Java существуют методы, называемые isJavaIdentifierStart и isJavaIdentifierPart в классе символов, которые могут использоваться для указания, является ли строка допустимым идентификатором Java, например:

public boolean isJavaIdentifier(String s) {
  int n = s.length();
  if (n==0) return false;
  if (!Character.isJavaIdentifierStart(s.charAt(0)))
      return false;
  for (int i = 1; i < n; i++)
      if (!Character.isJavaIdentifierPart(s.charAt(i)))
          return false;
  return true;
}

Есть ли что-то подобное для С#?

4b9b3361

Ответ 1

В основном что-то вроде:

const string start = @"(\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl})";
const string extend = @"(\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf})";
Regex ident = new Regex(string.Format("{0}({0}|{1})*", start, extend));
s = s.Normalize();
return ident.IsMatch(s);

Ответ 3

Я бы опасался других решений, предлагаемых здесь. Вызов CodeDomProvider.CreateProvider требует поиска и анализа файла Machine.Config, а также вашего файла app.config. Вероятно, это будет в несколько раз медленнее, чем время, необходимое для просто проверки строки.

Вместо этого я бы отстаивал одно из следующих изменений:

  • Кэш провайдера в статической переменной.

    Это приведет к удалению его создания только один раз, но это замедлит загрузку типа.

  • Создайте провайдера напрямую, создав экземпляр Microsoft.CSharp.CSharpCodeProvider.

    Это пропустит синтаксический анализ файла конфигурации.

  • Напишите код, чтобы реализовать проверку себя.

    Если вы это сделаете, вы получите максимальный контроль над тем, как это реализовано, что может помочь вам оптимизировать производительность, если вам нужно. См. Раздел 2.2.4 описания

Ответ 4

Недавно я написал метод расширения, который проверяет строку как допустимый идентификатор С#.

Вы можете найти суть с реализацией здесь: https://gist.github.com/FabienDehopre/5245476

Он основан на документации MSDN Identifier (http://msdn.microsoft.com/en-us/library/aa664670(v=vs.71).aspx)

public static bool IsValidIdentifier(this string identifier)
{
    if (String.IsNullOrEmpty(identifier)) return false;

    // C# keywords: http://msdn.microsoft.com/en-us/library/x53a06bb(v=vs.71).aspx
    var keywords = new[]
                       {
                           "abstract",  "event",      "new",        "struct",
                           "as",        "explicit",   "null",       "switch",
                           "base",      "extern",     "object",     "this",
                           "bool",      "false",      "operator",   "throw",
                           "breal",     "finally",    "out",        "true",
                           "byte",      "fixed",      "override",   "try",
                           "case",      "float",      "params",     "typeof",
                           "catch",     "for",        "private",    "uint",
                           "char",      "foreach",    "protected",  "ulong",
                           "checked",   "goto",       "public",     "unchekeced",
                           "class",     "if",         "readonly",   "unsafe",
                           "const",     "implicit",   "ref",        "ushort",
                           "continue",  "in",         "return",     "using",
                           "decimal",   "int",        "sbyte",      "virtual",
                           "default",   "interface",  "sealed",     "volatile",
                           "delegate",  "internal",   "short",      "void",
                           "do",        "is",         "sizeof",     "while",
                           "double",    "lock",       "stackalloc",
                           "else",      "long",       "static",
                           "enum",      "namespace",  "string"
                       };

    // definition of a valid C# identifier: http://msdn.microsoft.com/en-us/library/aa664670(v=vs.71).aspx
    const string formattingCharacter = @"\p{Cf}";
    const string connectingCharacter = @"\p{Pc}";
    const string decimalDigitCharacter = @"\p{Nd}";
    const string combiningCharacter = @"\p{Mn}|\p{Mc}";
    const string letterCharacter = @"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}";
    const string identifierPartCharacter = letterCharacter + "|" +
                                           decimalDigitCharacter + "|" +
                                           connectingCharacter + "|" +
                                           combiningCharacter + "|" +
                                           formattingCharacter;
    const string identifierPartCharacters = "(" + identifierPartCharacter + ")+";
    const string identifierStartCharacter = "(" + letterCharacter + "|_)";
    const string identifierOrKeyword = identifierStartCharacter + "(" +
                                       identifierPartCharacters + ")*";
    var validIdentifierRegex = new Regex("^" + identifierOrKeyword + "$", RegexOptions.Compiled);
    var normalizedIdentifier = identifier.Normalize();

    // 1. check that the identifier match the validIdentifer regex and it not a C# keyword
    if (validIdentifierRegex.IsMatch(normalizedIdentifier) && !keywords.Contains(normalizedIdentifier))
    {
        return true;
    }

    // 2. check if the identifier starts with @
    if (normalizedIdentifier.StartsWith("@") && validIdentifierRegex.IsMatch(normalizedIdentifier.Substring(1)))
    {
        return true;
    }

    // 3. it not a valid identifier
    return false;
}

Ответ 5

Некромирование здесь.

После просмотра в исходном коде mono:

CodeDomProvider.cs

public virtual bool IsValidIdentifier (string value) 
286         { 
287             ICodeGenerator cg = CreateGenerator (); 
288             if (cg == null) 
289                 throw GetNotImplemented (); 
290             return cg.IsValidIdentifier (value); 
291         } 
292  

Затем CSharpCodeProvider.cs

public override ICodeGenerator CreateGenerator() 
91      { 
92 #if NET_2_0 
93          if (providerOptions != null && providerOptions.Count > 0) 
94              return new Mono.CSharp.CSharpCodeGenerator (providerOptions); 
95 #endif 
96          return new Mono.CSharp.CSharpCodeGenerator(); 
97      } 

Затем CSharpCodeGenerator.cs

protected override bool IsValidIdentifier (string identifier)
{
    if (identifier == null || identifier.Length == 0)
        return false;

    if (keywordsTable == null)
        FillKeywordTable ();

    if (keywordsTable.Contains (identifier))
        return false;

    if (!is_identifier_start_character (identifier [0]))
        return false;

    for (int i = 1; i < identifier.Length; i ++)
        if (! is_identifier_part_character (identifier [i]))
            return false;

    return true;
}



private static System.Collections.Hashtable keywordsTable;
private static string[] keywords = new string[] {
    "abstract","event","new","struct","as","explicit","null","switch","base","extern",
    "this","false","operator","throw","break","finally","out","true",
    "fixed","override","try","case","params","typeof","catch","for",
    "private","foreach","protected","checked","goto","public",
    "unchecked","class","if","readonly","unsafe","const","implicit","ref",
    "continue","in","return","using","virtual","default",
    "interface","sealed","volatile","delegate","internal","do","is",
    "sizeof","while","lock","stackalloc","else","static","enum",
    "namespace",
    "object","bool","byte","float","uint","char","ulong","ushort",
    "decimal","int","sbyte","short","double","long","string","void",
    "partial", "yield", "where"
};


static void FillKeywordTable ()
{
    lock (keywords) {
        if (keywordsTable == null) {
            keywordsTable = new Hashtable ();
            foreach (string keyword in keywords) {
                keywordsTable.Add (keyword, keyword);
            }
        }
    }
}



static bool is_identifier_start_character (char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || Char.IsLetter (c);
}

static bool is_identifier_part_character (char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || Char.IsLetter (c);
}

Вы получаете этот код:

public static bool IsValidIdentifier (string identifier)
{
    if (identifier == null || identifier.Length == 0)
        return false;

    if (keywordsTable == null)
        FillKeywordTable();

    if (keywordsTable.Contains(identifier))
        return false;

    if (!is_identifier_start_character(identifier[0]))
        return false;

    for (int i = 1; i < identifier.Length; i++)
        if (!is_identifier_part_character(identifier[i]))
            return false;

    return true;
}


internal static bool is_identifier_start_character(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || char.IsLetter(c);
}

internal static bool is_identifier_part_character(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || char.IsLetter(c);
}


private static System.Collections.Hashtable keywordsTable;
private static string[] keywords = new string[] {
    "abstract","event","new","struct","as","explicit","null","switch","base","extern",
    "this","false","operator","throw","break","finally","out","true",
    "fixed","override","try","case","params","typeof","catch","for",
    "private","foreach","protected","checked","goto","public",
    "unchecked","class","if","readonly","unsafe","const","implicit","ref",
    "continue","in","return","using","virtual","default",
    "interface","sealed","volatile","delegate","internal","do","is",
    "sizeof","while","lock","stackalloc","else","static","enum",
    "namespace",
    "object","bool","byte","float","uint","char","ulong","ushort",
    "decimal","int","sbyte","short","double","long","string","void",
    "partial", "yield", "where"
};

internal static void FillKeywordTable()
{
    lock (keywords)
    {
        if (keywordsTable == null)
        {
            keywordsTable = new System.Collections.Hashtable();
            foreach (string keyword in keywords)
            {
                keywordsTable.Add(keyword, keyword);
            }
        }
    }
}

Ответ 6

С Roslyn является открытым исходным кодом, инструменты анализа кода прямо у вас под рукой, и они написаны для повышения производительности. (Сейчас они находятся в предварительном выпуске).

Однако я не могу говорить о стоимости исполнения загрузки сборки.

Установите инструменты с помощью nuget:

Install-Package Microsoft.CodeAnalysis -Pre

Задайте свой вопрос:

var isValid = Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier("I'mNotValid");
Console.WriteLine(isValid);     // False

Ответ 7

Теперь выпущенный Roslyn проект предоставляет Microsoft.CodeAnalysis.CSharp.SyntaxFacts, SyntaxFacts.IsIdentifierStartCharacter(char) и SyntaxFacts.IsIdentifierPartCharacter(char) методы, подобные Java.

Здесь он используется, в простой функции я использую для преобразования именных фраз (например, "Дата начала" ) в идентификаторы С# (например, "StartDate" ). N.B Я использую Humanizer, чтобы выполнить преобразование верблюда, и Roslyn, чтобы проверить, действительно ли символ.

    public static string Identifier(string name)
    {
        Check.IsNotNullOrWhitespace(name, nameof(name));

        // trim off leading and trailing whitespace
        name = name.Trim();

        // should deal with spaces => camel casing;
        name = name.Dehumanize();

        var sb = new StringBuilder();
        if (!SyntaxFacts.IsIdentifierStartCharacter(name[0]))
        {
            // the first characters 
            sb.Append("_");
        }

        foreach(var ch in name)
        {
            if (SyntaxFacts.IsIdentifierPartCharacter(ch))
            {
                sb.Append(ch);
            }
        }

        var result = sb.ToString();

        if (SyntaxFacts.GetKeywordKind(result) != SyntaxKind.None)
        {
            result = @"@" + result;
        }

        return result;
    }

Тесты;

    [TestCase("Start Date", "StartDate")]
    [TestCase("Bad*chars", "BadChars")]
    [TestCase("   leading ws", "LeadingWs")]
    [TestCase("trailing ws   ", "TrailingWs")]
    [TestCase("class", "Class")]
    [TestCase("int", "Int")]
    [Test]
    public void CSharp_GeneratesDecentIdentifiers(string input, string expected)
    {
        Assert.AreEqual(expected, CSharp.Identifier(input));
    }