Переписать метод IsHexString с помощью RegEx - PullRequest
1 голос
/ 08 февраля 2012

У меня есть метод, который проверяет , если строка допустимая шестнадцатеричная строка :

public bool IsHex(string value)
{
  if (string.IsNullOrEmpty(value) || value.Length % 2 != 0)
    return false;

  return 
    value.Substring(0, 2) == "0x" &&
    value.Substring(2)
      .All(c => (c >= '0' && c <= '9') ||
                (c >= 'a' && c <= 'f') ||
                (c >= 'A' && c <= 'F'));
}

Правила таковы:
Выражение должно состоять из четного числа шестнадцатеричных цифр ( 0-9, AF, af ).
Символы 0x должны быть первыми двумя символами в выражении.

Я уверен, что можно переписать в регулярном выражении гораздо чище и эффективнее.
Не могли бы вы помочь мне с этим?

Ответы [ 3 ]

6 голосов
/ 08 февраля 2012

После того как вы обновили свой вопрос, новое регулярное выражение, которое работает для вас, должно выглядеть следующим образом:

^0x(?:[0-9A-Fa-f]{2})+$

Где я использую (?: для группировки без захвата для эффективности.{2} означает, что вы хотите два предыдущих выражения (т.е. два шестнадцатеричных символа), + означает, что вы хотите один или несколько шестнадцатеричных символов.Обратите внимание, что это запрещает 0x в качестве допустимого значения.

Efficiency

«Одед» упомянул кое-что об эффективности.Я не знаю ваших требований, поэтому считаю это больше упражнением для ума, чем чем-либо еще.Регулярное выражение будет делать скачки до тех пор, пока наименьшее соответствующее регулярное выражение.Например, если я попробую собственное регулярное выражение на 10000 переменных строк ввода размером 50-5000 символов, все правильно, оно будет выполнено за 1,1 секунды.

Когда я попробую следующее регулярное выражение:

^0x(?:[0-9A-Fa-f]{32})+(?:[0-9A-Fa-f]{2})+$

он работает примерно на 40% быстрее, за 0,67 секунды.Но будь осторожен.Знание вашего вклада - это умение писать эффективные регулярные выражения.Например, если регулярное выражение завершится неудачей, будет выполнено много обратных проверок.Если половина моих входных строк имеет неправильную длину, время выполнения увеличивается примерно до 34 секунд, или 3000% (!), Для того же ввода.

Становится еще сложнее, если большинство входных строк большие.Если 99% вашего ввода имеет допустимую длину, все> 4130 символов, и только некоторые из них - нет, запись

^0x(?:[0-9A-Fa-f]{4096})+^0x(?:[0-9A-Fa-f]{32})+(?:[0-9A-Fa-f]{2})+$

эффективна и еще больше увеличивает время.Однако, если у многих есть неправильный length % 2 = 0, это неэффективно из-за обратного отслеживания.

Наконец, если большинство ваших строк удовлетворяют правилу четных чисел, и только некоторые или несколько строк содержат неправильный символ, скорость возрастает: чем больше входных данных содержит неправильный символ, тем выше производительность.Это потому, что, когда он находит недопустимый символ, он может сразу же всплыть.

Вывод: если ваш ввод смешанный, маленький, большой, неправильный символ, неправильный подсчет, ваш самый быстрый подход - использовать комбинацию проверкидлина строки (мгновенно в .NET) и использовать эффективное регулярное выражение.

3 голосов
/ 08 февраля 2012

Итак, в основном вы хотите проверить, начинается ли число с 0x и продолжается (непустой) последовательностью 0-9 и / или AF.Это можно легко указать как регулярное выражение:

return RegEx.IsMatch(value, "^0x[0-9A-Fa-f]+$")

Я не уверен, почему вы делаете проверку value.Length % 2 != 0 ... не является ли "0x1" действительным шестнадцатеричным числом?Кроме того, моя функция возвращает false на "0x", а ваша возвращает true.Если вы хотите изменить это, замените + (= один или много) на * (= ноль или много) в регулярном выражении.

РЕДАКТИРОВАТЬ: Теперь, когда вы оправили свое "четное числоТребование, я предлагаю вам использовать Abel's RegEx.Если вы сделаете это, я предлагаю вам вызвать ваш метод IsMsSqlHex или что-то подобное, чтобы документально подтвердить, что он не следует "обычным" шестнадцатеричным правилам.

2 голосов
/ 08 февраля 2012

Diatribe: Если вас беспокоит скорость, забудьте о Regex. Regex - это NFA , поэтому он в большинстве случаев медленнее, чем DFA или анализатор, написанный от руки.

Игнорирование того, что вы запросили Regex, может быть более эффективным (даже если ваша реализация, вероятно, в порядке - она ​​выделяет строки):

static bool IsHex(string value)
{
    if (string.IsNullOrEmpty(value) || value.Length < 3)
        return false;

    const byte State_Zero = 0;
    const byte State_X = 1;
    const byte State_Value = 2;

    var state = State_Zero;

    for (var i = 0; i < value.Length; i++)
    {
        switch (value[i])
        {
            case '0': 
                {
                    // Can be used in either Value or Zero.
                    switch (state)
                    {
                        case State_Zero: state = State_X; break;
                        case State_X: return false;
                        case State_Value: break;
                    }
                }
                break;
            case 'X': case 'x': 
                {
                    // Only valid in X.
                    switch (state)
                    {
                        case State_Zero: return false;
                        case State_X: state = State_Value; break;
                        case State_Value: return false;
                    }
                }
                break;
            case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
            case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
                {
                    // Only valid in Value.
                    switch (state)
                    {
                        case State_Zero: return false;
                        case State_X: return false;
                        case State_Value: break;
                    }
                }
                break;
            default: return false;
        }
    }

    return state == State_Value;
}

Если я могу правильно понять, чего вы пытаетесь достичь, возможно, эта функция лучше подойдет вашим потребностям:

static bool ParseNumber(string value, out int result)
{
    if (string.IsNullOrEmpty(value))
    {
        result = 0;
        return false;
    }

    if (value.StartsWith("0x"))
        return int.TryParse(value.Substring(2), NumberStyles.AllowHexSpecifier, null, out result);
    else
        return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
}

Только ради ударов Я пошел и профилировал :

Результаты на моем ноутбуке (выпуск / без отладчика):

  • Regex без скомпилированного / кэшированного Regex заняло 8137 мс (2x кэшировано, 20x рукописно)
  • Regex с скомпилированным / кэшированным Regex заняло 3463 мс (8 раз от руки)
  • Рукописное заняло 397мс (1x)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...