2009-12-14 2 views
19

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

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; 
} 

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

ответ

6

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

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); 
+4

OMG 7 upvotes, и он даже не работает, и даже не компилировался до тех пор, пока я не исправил код ... –

28

Да:

// using System.CodeDom.Compiler; 
CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"); 
if (provider.IsValidIdentifier (YOUR_VARIABLE_NAME)) { 
     // Valid 
} else { 
     // Not valid 
} 

Отсюда: How to determine if a string is a valid variable name?

+0

У этого есть некоторый перфекционизм. последствия, о которых вы должны знать. Пожалуйста, см. Мой пост для получения дополнительной информации. –

8

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

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

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

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

  2. Создание поставщика напрямую, путем создания экземпляра Microsoft.CSharp.CSharpCodeProvider вашей собственной личности

    Это пропустит конфигурационный файл разборе все вместе.

  3. Напишите код для осуществления проверки себя.

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

3

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

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

Это на основе документации MSDN по идентификатору (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's 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's not a valid identifier 
    return false; 
} 
3

Necromancing здесь.

In.NET Ядро/DNX, вы можете сделать это с Roslyn-SyntaxFacts

Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsReservedKeyword(
     Microsoft.CodeAnalysis.CSharp.SyntaxFacts.GetKeywordKind("protected") 
); 



foreach (ColumnDefinition cl in tableColumns) 
{ 
    sb.Append(@"   public "); 
    sb.Append(cl.DOTNET_TYPE); 
    sb.Append(" "); 

    // for keywords 
    //if (!Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(cl.COLUMN_NAME)) 
    if (Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsReservedKeyword(
     Microsoft.CodeAnalysis.CSharp.SyntaxFacts.GetKeywordKind(cl.COLUMN_NAME) 
     )) 
     sb.Append("@"); 

    sb.Append(cl.COLUMN_NAME); 
    sb.Append("; // "); 
    sb.AppendLine(cl.SQL_TYPE); 
} // Next cl 


или в старом варианте с CodeDOM - после взгляда в моно: исходный код

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  } 

Тогда C SharpCodeGenerator.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); 
      } 
     } 
    } 
} 
3

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

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

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

Install-Package Microsoft.CodeAnalysis -Pre 

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

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

Ныне выпустила Roslyn проект предусматривает Microsoft.CodeAnalysis.CSharp.SyntaxFacts с SyntaxFacts.IsIdentifierStartCharacter(char) и SyntaxFacts.IsIdentifierPartCharacter(char) методы так же, как Java.

Здесь он используется, в простой функции, используемой для преобразования именных фраз (например, «Дата начала») в идентификаторы C# (например, «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)); 
    } 
+0

Полезный факт, но не полезно, поскольку вы не объяснили, как использовать это. Я не могу найти пакет NuGet «Microsoft.CodeAnalysis», и я не могу найти официальную страницу, объясняющую, где можно получить библиотеку. – NightOwl888

+0

Я предоставил ссылку в первой настройке: https://github.com/dotnet/roslyn. Он отмечает: 'nuget install Microsoft.CodeAnalysis # Install Language APIs and Services' –

+0

Вы также должны установить' Microsoft.CodeAnalysis.CSharp', чтобы получить правила C#. –

Смежные вопросы