检查字符串是否可以解析的最快方法
我正在将CSV文件解析为具有强类型属性的对象列表。 这涉及使用TypeDescriptor
将文件中的每个字符串值解析为IConvertible
类型( int
, decimal
, double
, DateTime
等)。
我正在使用try catch
来处理解析失败时的情况。 然后记录发生此exception的位置和原因的确切细节以供进一步调查。 下面是实际解析代码:
try { parsedValue = TypeDescriptor.GetConverter(type).ConvertFromString(dataValue); } catch (Exception ex) { // Log failure }
问题:
成功解析值后,过程很快。 在解析具有大量无效数据的数据时,该过程可能会慢几千倍(由于捕获exception)。
我一直在测试这个解析到DateTime
。 这些是性能数据:
- 成功解析:每次解析平均32个滴答
- 解析失败:每次解析平均146296个滴答
这慢了4500多倍。
题:
是否有可能检查是否可以成功解析字符串值而无需使用我昂贵的try catch
方法? 或许还有另一种方法我应该这样做?
编辑:我需要使用TypeDescriptor
(而不是DateTime.TryParse
),因为类型是在运行时确定的。
如果您有一组已知的要转换的类型,您可以执行一系列if/elseif/elseif/else
(或类型名称上的switch/case
)以将其实际分发到专门的解析方法。 这应该很快。 这就像@Fabio的回答中所描述的那样。
如果仍然存在性能问题,还可以创建一个查找表,以便在需要时添加新的解析方法来支持它们:
给出一些基本的解析包装器:
public delegate bool TryParseMethod(string input, out T value); public interface ITryParser { bool TryParse(string input, out object value); } public class TryParser : ITryParser { private TryParseMethod ParsingMethod; public TryParser(TryParseMethod 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 Parsers; static DataConversion() { Parsers = new Dictionary(); AddParser(DateTime.TryParse); AddParser(Int32.TryParse); AddParser(Double.TryParse); AddParser(Decimal.TryParse); AddParser((string input, out string value) => {value = input; return true;}); } public static void AddParser(TryParseMethod parseMethod) { Parsers.Add(typeof(T), new TryParser (parseMethod)); } public static bool Convert (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("3", out value)) { //log failure } //or for unknown type at compile time: object value; if (!DataConversion.Convert(myType, dataValue, out value)) { //log failure }
这可能是为了避免object
拳击和类型转换而扩展generics,但是现在它的工作正常; 如果你有一个可衡量的表现,也许只会优化这个方面。
编辑:您可以更新DataConversion.Convert
方法,以便如果它没有注册指定的转换器,它可以回退到您的TypeConverter
方法或抛出适当的exception。 如果你想拥有一个全能或只是拥有预定义的一组支持类型并且避免重新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之前为每种类型构造正则表达式并将其应用于字符串? 您必须构建正则表达式,以便在字符串不匹配时,它不会解析。 如果字符串解析,这将会慢一点,因为你必须进行正则表达式测试,但如果它不解析它会更快。
您可以将正则表达式字符串放在Dictionary
,这将决定使用哪个正则表达式字符串。
您可以使用TryParse
方法:
if (DateTime.TryParse(input, out dateTime)) { Console.WriteLine(dateTime); }
这取决于。 如果您使用的是DateTime,则可以始终使用TryParse函数。 这将更快。