2009-12-14 3 views
19

En Java, il existe des méthodes appelées isJavaIdentifierStart et isJavaIdentifierPart sur la classe Character qui peuvent être utilisées pour indiquer si une chaîne est un identifiant Java valide, comme ceci:Existe-t-il une méthode en C# pour vérifier si une chaîne est un identificateur valide

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

Y a-t-il quelque chose comme ça pour C#?

Répondre

6

Fondamentalement, quelque chose comme:

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, et il ne fonctionne même pas, et n'a même pas compiler jusqu'à ce que j'ai corrigé le code ... –

28

Oui:

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

D'ici: How to determine if a string is a valid variable name?

+0

Ceci a quelques perf. implications dont vous devriez être conscient. S'il vous plaît voir mon poste pour plus d'informations. –

8

Je me méfie des autres solutions proposées ici. L'appel de CodeDomProvider.CreateProvider nécessite la recherche et l'analyse du fichier Machine.Config, ainsi que de votre fichier app.config. Cela risque d'être plusieurs fois plus lent que le temps nécessaire pour vérifier la chaîne de votre auto.

Au lieu de cela, je vous plaider en faveur de faire l'une des modifications suivantes:

  1. Cache le fournisseur dans une variable statique. Cela vous amènera à ne le créer qu'une seule fois, mais cela ralentira le chargement du type.

  2. Créer le fournisseur directement, en créant une instance Microsoft.CSharp.CSharpCodeProvider votre auto

    Cela va sauter le fichier de configuration analyse tous ensemble.

  3. Ecrivez le code pour implémenter le contrôle de votre auto. Si vous faites cela, vous aurez le plus grand contrôle sur la façon dont il est mis en œuvre, ce qui peut vous aider à optimiser les performances si vous en avez besoin. Voir la section 2.2.4 du C# language spec pour la grammaire lexicale complète pour les identificateurs C#.

3

Récemment, je l'ai écrit une méthode d'extension qui valide une chaîne comme un identifiant valide C#.

Vous pouvez trouver un point essentiel à la mise en œuvre ici: https://gist.github.com/FabienDehopre/5245476

Il est basé sur la documentation MSDN d'identificateur (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 ici.

Dans.NET de base/DNX, vous pouvez le faire avec 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 


Ou dans l'ancienne variante avec CodeDom - Après un coup d'oeil dans le code source 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 

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

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

Vous obtenez ce code:

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

Avec Roslyn étant open source, des outils d'analyse de code sont à portée de main, et ils sont écrits pour la performance. (En ce moment, ils sont en pré-sortie).

Cependant, je ne peux pas parler du coût de performance du chargement de l'assemblage.

installer les outils utilisant NuGet:

Install-Package Microsoft.CodeAnalysis -Pre 

Posez votre question:

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

Le projet Roslyn maintenant publié fournit Microsoft.CodeAnalysis.CSharp.SyntaxFacts, avec SyntaxFacts.IsIdentifierStartCharacter(char) et SyntaxFacts.IsIdentifierPartCharacter(char) méthodes tout comme Java.

Ici, il est utilisé, dans une fonction simple, j'utilise pour transformer des noms nominaux (par exemple "Date de début") en identificateurs C# (par exemple "StartDate"). N.B J'utilise Humanizer pour faire la conversion chameau, et Roslyn pour vérifier si un caractère est valide.

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

Tests;

[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

Fait utile, mais pas utile en ce que vous n'avez pas expliqué comment utiliser cela. Je n'arrive pas à trouver un paquet "Microsoft.CodeAnalysis" NuGet, et je ne peux pas trouver une page officielle expliquant où la bibliothèque peut être obtenue. – NightOwl888

+0

J'ai fourni le lien dans le premier setence: https://github.com/dotnet/roslyn. Il note: 'nuget install Microsoft.CodeAnalysis # Installer les API et services Language ' –

+0

Vous devez également installer' Microsoft.CodeAnalysis.CSharp' pour obtenir les règles C#. –

Questions connexes