2009-12-14 11 views
19

W języku Java istnieją metody o nazwie isJavaIdentifierStart i isJavaIdentifierPart w klasie Character, które mogą być używane do sprawdzenia, czy ciąg znaków jest poprawnym identyfikatorem Java, jak na przykład:Czy istnieje metoda w języku C# do sprawdzania, czy łańcuch jest poprawnym identyfikatorem

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

Czy jest coś takiego dla C#?

Odpowiedz

6

Zasadniczo coś takiego:

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

Upominki OMG 7, i to nawet nie działa, a nawet nie skompilowałem dopóki nie naprawiłem kodu ... –

28

Tak:

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

stąd: How to determine if a string is a valid variable name?

+0

To ma trochę perfekcji. implikacje, których powinieneś być świadomy. Zobacz mój post, aby uzyskać więcej informacji. –

8

Byłbym ostrożny z innych rozwiązań oferowanych tutaj. Wywołanie CodeDomProvider.CreateProvider wymaga znalezienia i przeanalizowania pliku Machine.Config, a także pliku app.config. Prawdopodobnie będzie to kilka razy wolniej niż czas wymagany do sprawdzenia samego ciągu znaków.

Zamiast tego chciałbym opowiadają zrobić jedną z następujących zmian:

  1. cache dostawcą w zmiennej statycznej.

    Spowoduje to, że zostaniesz trafiony tylko raz, ale spowolni to ładowanie.

  2. Tworzenie dostawcy bezpośrednio, tworząc instancję Microsoft.CSharp.CSharpCodeProvider Twój własny

    Pozwoli to pominąć plik konfiguracyjny analizowania wszystkich razem.

  3. Napisz kod, aby zaimplementować sprawdzanie samego siebie.

    Jeśli to zrobisz, uzyskasz największą kontrolę nad jego implementacją, co pomoże Ci zoptymalizować wydajność, jeśli zajdzie taka potrzeba. Zobacz rozdział 2.2.4 z C# language spec dla kompletnej gramatyki leksykalnej dla identyfikatorów C#.

3

Niedawno pisałem metodę rozszerzenia, które sprawdza ciąg jako ważnego identyfikatora C#.

można znaleźć sens z realizacji tu: https://gist.github.com/FabienDehopre/5245476

Jest on oparty na dokumentacji MSDN identyfikatora (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 tutaj.

In.NET Rdzeń/DNX, można to zrobić z 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 


lub w starym wariancie z Codedom - w końcu spojrzeć w źródłowego 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 

Następnie 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  } 

następnie 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); 
} 

dostać ten kod:

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

Z Roslyn jest open source, narzędzia do analizy kodu są w zasięgu ręki, a oni napisany dla wydajności. (W tej chwili są w wersji wstępnej).

Jednak nie mogę mówić o kosztach wydajności ładowania zespołu.

Instalowanie za pomocą narzędzia Nuget:

Install-Package Microsoft.CodeAnalysis -Pre 

Zadaj pytanie:

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

obecnie wydany Roslyn Projekt przewiduje Microsoft.CodeAnalysis.CSharp.SyntaxFacts z SyntaxFacts.IsIdentifierStartCharacter(char) i SyntaxFacts.IsIdentifierPartCharacter(char) metod podobnie jak Java.

Tutaj jest w użyciu, w prostej funkcji używam, aby zamienić wyrażenia rzeczownikowe (np. "Data rozpoczęcia") na identyfikatory C# (np. "StartDate"). N.B Używam Humanizer do konwersji wielbłądów, a Roslyn do sprawdzenia, czy znak jest ważny.

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

Badania;

[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

Przydatny fakt, ale nie jest pomocny, ponieważ nie wyjaśniłeś, jak go wykorzystać. Nie mogę zlokalizować pakietu NuGet "Microsoft.CodeAnalysis", ani nie mogę znaleźć oficjalnej strony wyjaśniającej, gdzie można uzyskać bibliotekę. – NightOwl888

+0

Podałem link w pierwszej konfiguracji: https://github.com/dotnet/roslyn. Zauważa: 'nuget install Microsoft.CodeAnalysis # Zainstaluj interfejs API i usługi językowe' –

+0

Powinieneś zainstalować 'Microsoft.CodeAnalysis.CSharp', aby uzyskać reguły C#. –

Powiązane problemy