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

Самый быстрый способ проверить, может ли строка разбираться

Я разбираю файлы CSV в списках объектов с сильно типизированными свойствами. Это включает в себя разбор каждого строкового значения из файла на тип IConvertible (int, decimal, double, DateTime и т.д.) С помощью TypeDescriptor.

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

try
{
    parsedValue = TypeDescriptor.GetConverter(type).ConvertFromString(dataValue);
}
catch (Exception ex)
{
    // Log failure
}

Проблема:

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

Я тестировал это с помощью парсинга DateTime. Это показатели производительности:

  • Успешный синтаксический анализ: средний 32 тика за разбор
  • Неудачный синтаксический анализ: средний 146296 тиков за разбор

Это более чем в 4500 раз медленнее.

Вопрос:

Возможно ли, чтобы я проверил, можно ли корректно проанализировать строковое значение без использования моего дорогого метода try catch? Или, возможно, есть другой способ, которым я должен это делать?

EDIT: мне нужно использовать TypeDescriptor (а не DateTime.TryParse), потому что тип определяется во время выполнения.

4b9b3361

Ответ 1

Если у вас есть известный набор типов для преобразования, вы можете сделать серию if/elseif/elseif/else (или switch/case в имени типа), чтобы по существу распространить ее на специализированные методы синтаксического анализа. Это должно быть довольно быстро. Это описано в @Fabio answer.

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

Учитывая некоторые основные синтаксические разборки:

public delegate bool TryParseMethod<T>(string input, out T value);

public interface ITryParser
{
    bool TryParse(string input, out object value);
}

public class TryParser<T> : ITryParser
{
    private TryParseMethod<T> ParsingMethod;

    public TryParser(TryParseMethod<T> parsingMethod)
    {
        this.ParsingMethod = parsingMethod;
    }

    public bool TryParse(string input, out object value)
    {
        T parsedOutput;
        bool success = ParsingMethod(input, out parsedOutput);
        value = parsedOutput;
        return success;
    }
}

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

public static class DataConversion
{
    private static Dictionary<Type, ITryParser> Parsers;

    static DataConversion()
    {
        Parsers = new Dictionary<Type, ITryParser>();
        AddParser<DateTime>(DateTime.TryParse);
        AddParser<int>(Int32.TryParse);
        AddParser<double>(Double.TryParse);
        AddParser<decimal>(Decimal.TryParse);
        AddParser<string>((string input, out string value) => {value = input; return true;});
    }

    public static void AddParser<T>(TryParseMethod<T> parseMethod)
    {
        Parsers.Add(typeof(T), new TryParser<T>(parseMethod));
    }

    public static bool Convert<T>(string input, out T value)
    {
        object parseResult;
        bool success = Convert(typeof(T), input, out parseResult);
        if (success)
            value = (T)parseResult;
        else
            value = default(T);
        return success;
    }

    public static bool Convert(Type type, string input, out object value)
    {
        ITryParser parser;
        if (Parsers.TryGetValue(type, out parser))
            return parser.TryParse(input, out value);
        else
            throw new NotSupportedException(String.Format("The specified type \"{0}\" is not supported.", type.FullName));
    }
}

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

//for a known type at compile time
int value;
if (!DataConversion.Convert<int>("3", out value))
{
    //log failure
}

//or for unknown type at compile time:
object value;
if (!DataConversion.Convert(myType, dataValue, out value))
{
    //log failure
}

Вероятно, возможно, что генераторы расширены, чтобы избежать object бокса и типа литья, но по мере того, как это происходит, это отлично работает; возможно, только оптимизировать этот аспект, если у вас есть измеримая производительность.

EDIT: вы можете обновить метод DataConversion.Convert, чтобы, если у него нет зарегистрированного зарегистрированного конвертера, он может вернуться к вашему методу TypeConverter или выбросить соответствующее исключение. Это зависит от вас, если вы хотите иметь уловки или просто иметь предопределенный набор поддерживаемых типов и избегать повторного использования try/catch. Как бы то ни было, код обновлен, чтобы выбросить NotSupportedException с сообщением о неподдерживаемом типе. Не стесняйтесь настраивать, поскольку это имеет смысл. Производительность мудрая, может быть, имеет смысл делать ловушки, поскольку, возможно, их будет меньше и далеко друг от друга, как только вы укажете специализированные парсеры для наиболее часто используемых типов.

Ответ 2

Если вам известен тип, в котором вы пытаетесь разобрать, используйте метод TryParse:

String value;
Int32 parsedValue;
if (Int32.TryParse(value, parsedValue) == True)
    // actions if parsed ok
else
    // actions if not parsed

То же самое для других типов

Decimal.TryParse(value, parsedValue)
Double.TryParse(value, parsedValue)
DateTime.TryParse(value, parsedValue)

Или вы можете использовать следующий обходной путь:

Создайте методы анализа для каждого типа с тем же именем, но с другой подписью (оберните TryParse внутри них):

Private bool TryParsing(String value, out Int32 parsedValue)
{
    Return Int32.TryParse(value, parsedValue)
}

Private bool TryParsing(String value, out Double parsedValue)
{
    Return Double.TryParse(value, parsedValue)
}

Private bool TryParsing(String value, out Decimal parsedValue)
{
    Return Decimal.TryParse(value, parsedValue)
}

Private bool TryParsing(String value, out DateTime parsedValue)
{
    Return DateTime.TryParse(value, parsedValue)
}

Затем вы можете использовать метод TryParsing с вашими типами

Ответ 3

Вы можете использовать метод TryParse:

if (DateTime.TryParse(input, out dateTime))
{
    Console.WriteLine(dateTime);
}

Ответ 4

Как построить регулярное выражение для каждого типа и применить его к строке перед вызовом Parse? Вам нужно будет создать регулярное выражение, чтобы, если строка не соответствует, она не будет анализировать. Это будет немного медленнее, если строка будет анализироваться, так как вам придется выполнять тест регулярных выражений, но это будет быстрее, если оно не будет анализироваться.

Вы можете поместить строки регулярных выражений в Dictionary<Type, string>, чтобы определить, какая строка регулярного выражения используется просто.

Ответ 5

Это зависит. Если вы используете DateTime, вы всегда можете использовать функцию TryParse. Это будет на величину быстрее.