2009-12-14 13 views
19

In Java esistono metodi chiamati isJavaIdentifierStart e isJavaIdentifierPart nella classe Character che può essere utilizzata per stabilire se una stringa è un identificatore Java valido, ad esempio:Esiste un metodo in C# per verificare se una stringa è un identificatore valido

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'è qualcosa come questo per C#?

risposta

6

In sostanza qualcosa di simile:

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, e non funziona nemmeno, e non ha nemmeno compilato finché non ho risolto il codice ... –

28

Sì:

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

Da qui: How to determine if a string is a valid variable name?

+0

Questo ha un po 'di perf. implicazioni di cui dovresti essere a conoscenza. Si prega di consultare il mio post per maggiori informazioni. –

8

sarei diffidare delle altre soluzioni offerte qui. Chiamare CodeDomProvider.CreateProvider richiede di trovare e analizzare il file Machine.Config, nonché il file app.config. È probabile che sia più lento del tempo richiesto per controllare la stringa.

Invece io sosterrei si effettua una delle seguenti modifiche:

  1. cache del provider in una variabile statica.

    Questo ti farà prendere il colpo di crearlo solo una volta, ma rallenterà il caricamento del tipo.

  2. Creare il fornitore direttamente, con la creazione di un'istanza Microsoft.CSharp.CSharpCodeProvider vostra auto

    Questa salterà il file di configurazione parsing tutti insieme.

  3. Scrivi il codice per implementare l'assegno.

    Se si esegue questa operazione, si ottiene il massimo controllo su come è implementato, il che può aiutare a ottimizzare le prestazioni se necessario. Vedere la sezione 2.2.4 di C# language spec per la grammatica lessicale completa per gli identificatori C#.

3

Recentemente, ho scritto un metodo di estensione che convalida una stringa come C# identificatore valido.

potete trovare una sostanza con l'implementazione qui: https://gist.github.com/FabienDehopre/5245476

Si basa sulla documentazione MSDN di 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'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 qui.

In.Nucleo NET/DNX, è possibile farlo con 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 


O nella vecchia variante con CodeDOM - Dopo uno sguardo al codice sorgente 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 

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

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

Si ottiene questo codice:

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

Con Roslyn essendo open source, strumenti di analisi del codice sono a portata di mano, e sono scritte per le prestazioni. (In questo momento sono in pre-rilascio).

Tuttavia, non posso parlare del costo delle prestazioni di caricamento dell'assieme.

installare gli strumenti tramite NuGet:

Install-Package Microsoft.CodeAnalysis -Pre 

vostre domande:

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

Il Roslyn progetto ormai rilasciato fornisce Microsoft.CodeAnalysis.CSharp.SyntaxFacts, con SyntaxFacts.IsIdentifierStartCharacter(char) e SyntaxFacts.IsIdentifierPartCharacter(char) metodi proprio come Java.

Qui è in uso, in una semplice funzione che uso per trasformare le frasi nominali (ad esempio "Data di inizio") in identificatori C# (ad esempio "StartDate"). N.B Sto usando Humanizer per fare la conversione del caso cammello, e Roslyn per verificare se un personaggio è valido.

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

Test;

[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

Un fatto utile, ma non utile in quanto non hai spiegato come utilizzarlo. Non riesco a trovare un pacchetto NuGet "Microsoft.CodeAnalysis", né riesco a trovare una pagina ufficiale che spieghi dove è possibile ottenere la libreria. – NightOwl888

+0

Ho fornito il collegamento nel primo setence: https://github.com/dotnet/roslyn. Nota: 'nuget installa Microsoft.CodeAnalysis # Install Language APIs and Services' –

+0

Dovresti installare' Microsoft.CodeAnalysis.CSharp' per ottenere anche le regole C#. –

Problemi correlati