检查字符串是否可以解析的最快方法
本文关键字:方法 字符串 是否 可以解 检查 | 更新日期: 2023-09-27 18:15:58
我正在将CSV文件解析为具有强类型属性的对象列表。这包括使用TypeDescriptor
将文件中的每个字符串值解析为IConvertible
类型(int
、decimal
、double
、DateTime
等)。
我使用try catch
来处理解析失败的情况。然后记录异常发生的位置和原因的确切细节,以便进一步调查。下面是实际的解析代码:
try
{
parsedValue = TypeDescriptor.GetConverter(type).ConvertFromString(dataValue);
}
catch (Exception ex)
{
// Log failure
}
问题:
当值解析成功时,过程很快。当解析包含大量无效数据的数据时,该过程可能会慢数千倍(由于捕获异常)。
我一直在测试这与解析到DateTime
。这些是性能数据:
- 解析成功:每次解析平均32个tick
- 解析失败:每次解析平均146296个tick
这比原来慢了4500倍。
问题:
我是否有可能检查一下,看看字符串值是否可以成功解析,而不必使用昂贵的try catch
方法?或者我应该用另一种方式来做这件事?
编辑:我需要使用TypeDescriptor
(而不是DateTime.TryParse
),因为类型是在运行时确定的
如果您有一组已知的类型要转换,您可以执行一系列的if/elseif/elseif/else
(或类型名称上的switch/case
)来将其分发给专门的解析方法。这应该很快。这是@Fabio的回答中所描述的。
如果你仍然有性能问题,你也可以创建一个查找表,它可以让你添加新的解析方法,因为你需要支持它们:
给出一些基本的解析包装器:
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
装箱和类型转换,但就目前而言,这工作得很好;也许只有当你从中获得可衡量的性能时,才能优化这方面。
编辑:您可以更新DataConversion.Convert
方法,以便如果它没有注册指定的转换器,它可以回退到您的TypeConverter
方法或抛出适当的异常。这取决于您是想要一个包揽一切的类型,还是仅仅拥有预定义的支持类型集,从而避免再次使用try/catch
。目前,代码已经更新为抛出一个NotSupportedException
,其中包含一条指示不支持类型的消息。你可以随意调整,因为它是有意义的。在性能方面,也许做一个全包是有意义的,因为一旦您为最常用的类型指定了专门的解析器,这些解析器可能会越来越少。
如果您知道要解析的类型,则使用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
方法来处理你的类型
如何为每种类型构造一个正则表达式并在调用Parse之前将其应用于字符串?您必须构建这样的正则表达式:如果字符串不匹配,它将无法解析。如果字符串被解析,这会稍微慢一点,因为你必须做regex测试,但如果不被解析,它会快得多。
您可以将正则表达式字符串放在Dictionary<Type, string>
中,这将使确定使用哪个正则表达式字符串变得简单。
您可以使用TryParse
方法:
if (DateTime.TryParse(input, out dateTime))
{
Console.WriteLine(dateTime);
}
看情况。如果使用的是DateTime,则总是可以使用TryParse函数。这个会快一个数量级。