Existe um método em C # para verificar se uma string é um identificador válido [duplicado]
-
19-09-2019 - |
Pergunta
Esta questão já tem uma resposta aqui:
Em Java, existem métodos chamados isJavaIdentifierStart
e isJavaIdentifierPart
na classe de caracteres que pode ser usado para dizer se uma string é um identificador Java válido, assim:
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;
}
Existe algo parecido com isso para C #?
Solução
Basicamente algo como:
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);
Outras dicas
Sim:
// using System.CodeDom.Compiler;
CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
if (provider.IsValidIdentifier (YOUR_VARIABLE_NAME)) {
// Valid
} else {
// Not valid
}
A partir daqui: Como determinar se uma string é um nome de variável válido?
Eu seria cauteloso com as outras soluções oferecidas aqui. Chamando CodeDomProvider.CreateProvider exige encontrar e analisar o arquivo Machine.Config, bem como o seu arquivo app. Que é susceptível de ser várias vezes mais lento do que o tempo necessário para apenas verificar a seqüência de seu self.
Em vez disso eu defendo você faz uma das seguintes alterações:
-
Cache o provedor em uma variável estática.
Isso fará com que você a aproveitar o sucesso de criá-lo apenas uma vez, mas ele vai abrandar tipo de carregamento.
-
Criar o provedor diretamente, criando uma instância Microsoft.CSharp.CSharpCodeProvider sua auto
Este irá ignorar o arquivo de configuração analisar todos juntos.
-
Escreva o código para implementar a verificação de seu self.
Se você fizer isso, você obter o maior controle sobre como ele é implementado, o que pode ajudá-lo a otimizar o desempenho se você precisa. Consulte a seção 2.2.4 do linguagem C # especificação para a gramática lexical completo para C # identificadores.
Com Roslyn ser open source, ferramentas de análise de código estão na ponta dos dedos, e eles são escritos para o desempenho. (Agora eles estão em pré-lançamento).
No entanto, não posso falar com o custo de carregar o assembly desempenho.
Instale as ferramentas usando NuGet:
Install-Package Microsoft.CodeAnalysis -Pre
Faça sua pergunta:
var isValid = Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier("I'mNotValid");
Console.WriteLine(isValid); // False
Necromancing aqui.
Em .NET Núcleo / DNX, você pode fazê-lo com 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 na antiga variante com CodeDom - Depois de um olhar no mono código fonte:
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
Então 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 }
Então 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);
}
Você recebe este código:
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);
}
}
}
}
Recentemente, escrevi um método de extensão que valida uma string como um identificador válido C #.
Você pode encontrar uma essência com a implementação aqui: https://gist.github.com/FabienDehopre/ 5245476
É com base na documentação MSDN de 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;
}
O agora lançado projeto Roslyn fornece Microsoft.CodeAnalysis.CSharp.SyntaxFacts
, com SyntaxFacts.IsIdentifierStartCharacter(char)
e SyntaxFacts.IsIdentifierPartCharacter(char)
métodos apenas como Java.
Aqui ele está em uso, em uma função simples que eu uso para ligar substantivo frases (por exemplo "Data de Início") em C # identificadores (por exemplo, "StartDate"). NB eu estou usando Humanizer para fazer a conversão camel-case, e Roslyn para verificar se um personagem é válido .
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;
}
testes;
[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));
}
Isto pode ser feito usando a reflexão - veja Como determinar se uma string é um nome de variável válido?