Подтвердить что ты не робот

Как определить, является ли строка допустимым именем переменной?

Я ищу быстрый способ (в С#), чтобы определить, является ли строка допустимым именем переменной. Моя первая интуиция заключается в том, чтобы поднять некоторое регулярное выражение, чтобы сделать это, но мне интересно, есть ли лучший способ сделать это. Как, может быть, какой-то секретный метод, спрятанный где-то под названием IsThisAValidVariableName (имя строки) или какой-то другой способ сделать это, который не подвержен ошибкам, которые могут возникнуть из-за нехватки регулярного выражения.

4b9b3361

Ответ 1

Попробуйте следующее:

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

Ответ 2

  public static bool IsIdentifier(string text)
  {
     if (string.IsNullOrEmpty(text))
        return false;
     if (!char.IsLetter(text[0]) && text[0] != '_')
        return false;
     for (int ix = 1; ix < text.Length; ++ix)
        if (!char.IsLetterOrDigit(text[ix]) && text[ix] != '_')
           return false;
     return true;
  }

Ответ 3

Есть несколько особых случаев вокруг символа @, которые легко забыть проверить, а именно, '@' сам по себе не является допустимым идентификатором, и ни один из них не является "@1foo". Чтобы поймать их, вы можете сначала проверить, является ли строка ключевым словом, а затем удалить @ с начала строки, а затем проверить, является ли допустимым идентификатор слева (запрет @).

Здесь я объединил это с методом для разбора последовательностей Unicode в идентификаторах и, надеюсь, завершил проверку символов Unicode С# (5.0). Чтобы использовать его, сначала вызовите TryParseRawIdentifier() для обработки ключевых слов, escape-последовательностей, символов форматирования (которые удаляются) и стенографических идентификаторов. Затем передайте результат в IsValidParsedIdentifier(), чтобы проверить, являются ли первые и последующие символы действительными. Обратите внимание, что строки, возвращаемые из TryParseRawIdentifier(), равны тогда и только тогда, когда идентификаторы считаются идентичными с помощью С#.

public static class CSharpIdentifiers
{
    private static HashSet<string> _keywords = new HashSet<string> {
        "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked",
        "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else",
        "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for",
        "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
        "long", "namespace", "new", "null", "object", "operator", "out", "override", "params",
        "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed",
        "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw",
        "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
        "virtual", "void", "volatile", "while"
    };

    public static IReadOnlyCollection<string> Keywords { get { return _keywords; } }


    public static bool TryParseRawIdentifier(string str, out string parsed)
    {
        if (string.IsNullOrEmpty(str) || _keywords.Contains(str)) { parsed = null; return false; }

        StringBuilder sb = new StringBuilder(str.Length);

        int verbatimCharWidth = str[0] == '@' ? 1 : 0;

        for (int i = verbatimCharWidth; i < str.Length; ) //Manual increment
        {
            char c = str[i];

            if (c == '\\')
            {
                char next = str[i + 1];

                int charCodeLength;
                if (next == 'u') charCodeLength = 4;
                else if (next == 'U') charCodeLength = 8;
                else { parsed = null; return false; }
                //No need to check for escaped backslashes or special sequences like \n,
                //as they not valid identifier characters

                int charCode;
                if (!TryParseHex(str.Substring(i + 2, charCodeLength), out charCode)) { parsed = null; return false; }

                sb.Append(char.ConvertFromUtf32(charCodeLength)); //Handle characters above 2^16 by converting them to a surrogate pair
                i += 2 + charCodeLength;
            }
            else if (char.GetUnicodeCategory(str, i) == UnicodeCategory.Format)
            {
                //Use (string, index) in order to handle surrogate pairs
                //Skip this character
                if (char.IsSurrogatePair(str, i)) i += 2;
                else i += 1;
            }
            else
            {
                sb.Append(c);
                i++;
            }
        }

        parsed = sb.ToString();
        return true;
    }

    private static bool TryParseHex(string str, out int result)
    {
        return int.TryParse(str, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out result);
        //NumberStyles.AllowHexSpecifier forces all characters to be hex digits
    }

    public static bool IsValidParsedIdentifier(string str)
    {
        if (string.IsNullOrEmpty(str)) return false;

        if (!IsValidParsedIdentifierStart(str, 0)) return false;

        int firstCharWidth = char.IsSurrogatePair(str, 0) ? 2 : 1;

        for (int i = firstCharWidth; i < str.Length; ) //Manual increment
        {
            if (!IsValidParsedIdentifierPart(str, i)) return false;
            if (char.IsSurrogatePair(str, i)) i += 2;
            else i += 1;
        }

        return true;
    }

    //(String, index) pairs are used instead of chars in order to support surrogate pairs
    //(Unicode code-points above 2^16 represented using two 16-bit characters)

    public static bool IsValidParsedIdentifierStart(string s, int index)
    {
        return s[index] == '_' || char.IsLetter(s, index) || char.GetUnicodeCategory(s, index) == UnicodeCategory.LetterNumber;
    }

    public static bool IsValidParsedIdentifierPart(string s, int index)
    {
        if (s[index] == '_' || (s[index] >= '0' && s[index] <= '9') || char.IsLetter(s, index)) return true;

        switch (char.GetUnicodeCategory(s, index))
        {
            case UnicodeCategory.LetterNumber: //Eg. Special Roman numeral characters (not covered by IsLetter())
            case UnicodeCategory.DecimalDigitNumber: //Includes decimal digits in other cultures
            case UnicodeCategory.ConnectorPunctuation:
            case UnicodeCategory.NonSpacingMark:
            case UnicodeCategory.SpacingCombiningMark:
            //UnicodeCategory.Format handled in TryParseRawIdentifier()
                return true;
            default:
                return false;
        }
    }
}

Ответ 4

Более длинный путь, а также гораздо медленнее, заключается в использовании отражения для итерации над членами класса/пространства имен и сравнения, проверяя, является ли отраженный элемент **. ToString() ** совпадает с строковым вводом, для этого требуется, чтобы сборка была загружена заранее.

Другой способ сделать это (гораздо более длинный путь вокруг него, который превосходит использование регулярного выражения, используя уже доступный сканер/парсер Antlr) ограничивает синтаксический анализ/лексирование кода С#, а затем сканирование для имен членов (т.е. переменных) и по сравнению со строкой, используемой в качестве ввода, например, введите строку с именем "fooBar", затем укажите источник (например, сборку или код С#) и сканируйте его, проанализировав поиск специально для объявления элементов, например,

private int fooBar;

Да, это сложное, но мощное понимание возникнет, когда вы поймете, что делают авторы компилятора, и повысит ваши знания языка С# до уровня, где вы получите достаточно интимный синтаксис и его особенности.

Ответ 5

В WPF это можно использовать для проверки, является ли строка допустимым именем переменной. Но он не распознает зарезервированные строки, такие как "public".

// works only in WPF!
public static bool CheckIfStringIsValidVarName(string stringToCheck)
{
    if (string.IsNullOrWhiteSpace(stringToCheck))
        return false;

    TextBox textBox = new TextBox();

    try
    {
        // stringToCheck == ""; // !!! does NOT throw !!!
        // stringToCheck == "Name$"; // throws
        // stringToCheck == "0"; // throws
        // stringToCheck == "name with blank"; // throws
        // stringToCheck == "public"; // does NOT throw
        // stringToCheck == "ValidName";

        textBox.Name = stringToCheck;
    }
    catch (ArgumentException ex)
    {
        return false;
    }

    return true;
}