C# DateTime.ParseExact的更快替代方案
我数百万次将字符串解析为日期时间:C# DateTime.ParseExact的更快替代方案,c#,parsing,datetime,C#,Parsing,Datetime,我数百万次将字符串解析为日期时间: public static CultureInfo ci = CultureInfo.InvariantCulture; while (!reader.EndOfStream) { line = reader.ReadLine(); string[] fields = line.Split(' '); DateTime newDT = DateTime.ParseExact(fields[0], "yyyyMMd
public static CultureInfo ci = CultureInfo.InvariantCulture;
while (!reader.EndOfStream)
{
line = reader.ReadLine();
string[] fields = line.Split(' ');
DateTime newDT = DateTime.ParseExact(fields[0], "yyyyMMddTHHmmssfff", ci);
}
我的剖析器突出显示了ParseExact占用了大量时间。是否有其他方法/途径可以将字符串解析为更快的日期时间
后续行动1:
1) 我试过了,但速度还是一样的
bool OK = DateTime.TryParseExact(fields[0], "yyyyMMddTHHmmssfff", null, System.Globalization.DateTimeStyles.None,out DT);
(二)
我尝试编写自己的解析器,但速度太慢了:
public static DateTime fastParse(ref string s)
{
return new DateTime(int.Parse(s.Substring(0,4)), int.Parse(s.Substring(4,2)),int.Parse(s.Substring(6,2)), int.Parse(s.Substring(9,2)),int.Parse(s.Substring(11,2)),int.Parse(s.Substring(13,2)),int.Parse(s.Substring(15, 3)));
}
后续行动2
我尝试了一个存储值的建议——同样它也没有更快——也许问题在于构造
public class fastParseData
{
int year;
int mon;
int day;
int hour;
int min;
string previousSlice = "";
public DateTime fastParse(ref string s)
{
if (previousSlice != s.Substring(0, 12))
{
year=int.Parse(s.Substring(0,4));
mon=int.Parse(s.Substring(4,2));
day=int.Parse(s.Substring(6,2));
hour= int.Parse(s.Substring(9,2));
min = int.Parse(s.Substring(11,2));
previousSlice = s.Substring(0, 12);
}
return new DateTime(year, mon, day, hour,min, int.Parse(s.Substring(13, 2)), int.Parse(s.Substring(15, 3)));
}
}
折叠3
public class fastParseData
{
int year;
int mon;
int day;
int hour;
int min;
string previousSlice = "";
DateTime previousDT;
public DateTime fastParse(ref string s)
{
if (previousSlice != s.Substring(0, 12))
{
year=int.Parse(s.Substring(0,4));
mon=int.Parse(s.Substring(4,2));
day=int.Parse(s.Substring(6,2));
hour= int.Parse(s.Substring(9,2));
min = int.Parse(s.Substring(11,2));
previousSlice = s.Substring(0, 12);
previousDT = new DateTime(year, mon, day, hour,min,0,0);
}
return previousDT.AddMilliseconds((int.Parse(s.Substring(13, 2))*1000)+int.Parse(s.Substring(15, 3)));
}
}
后续行动4
从我的剖析器来看,问题的关键似乎是
int.Parse(s.Substring(13, 2))
解析位比子字符串更昂贵
我试过了
int.TryParse(s.Substring(13, 2),NumberStyles.None,ci, out secs)
Convert.ToInt32(s.Substring(13, 2));
但是再一次——速度没有差别
有没有更快的方法来解析int?您可以编写自己的解析算法,首先将字符串拆分为数组/列表/任何内容,然后使用Datetime构造函数创建Datetime
DateTime newDT = DateTime(Int32, Int32, Int32, Int32, Int32, Int32, Int32);
由于年/月/日的变化不会那么快,因此可以对它们进行缓冲,从而减少字符串操作的数量
一种简单的方法是存储前8个字母,如字符串a=fields[0]。切片(0,8)(目前不知道正确的操作),现在解析它们并生成int,但在下一次运行中,再次切片并测试a=new a,如果是,则使用上次的int,而不是再次解析它们,当然,你需要存储a和整数 因此,由于现在的问题似乎是构造时间,您应该尝试通过使用addSecond等检查INT是否高于/低于之前的值来添加经过的时间,或者您可以使用构造并将值设置为新时间 试试这个:
public class fastParseData
{
int year;
int mon;
int day;
int hour;
int min;
string previousSlice = "";
DateTime previousDT;
public DateTime fastParse(ref string s)
{
if (previousSlice != s.Substring(0, 12))
{
year=int.Parse(s.Substring(0,4));
mon=int.Parse(s.Substring(4,2));
day=int.Parse(s.Substring(6,2));
hour= int.Parse(s.Substring(9,2));
min = int.Parse(s.Substring(11,2));
previousSlice = s.Substring(0, 12);
previousDT = new DateTime(year, mon, day, hour,min,0,0);
}
return previousDT.ParseExact(year, mon, day, hour,min, int.Parse(s.Substring(13, 2)), int.Parse(s.Substring(15, 3));
}
}
这样,您只需要创建一个DT,然后设置新的时间,这可能是一个使用并行化的好地方。ForEach可能是一个很好的用途,但您可能需要测试其他实现并行化的方法
private static IEnumerable<string> GetLines(TextReader reader)
{
while (!reader.EndOfStream)
{
yield return reader.ReadLine();
}
}
private static CultureInfo ci = CultureInfo.InvariantCulture;
public static ConcurrentBag ProcessData(TextReader reader)
{
ConcurrentBag <DateTime> results = new ConcurrentBag <DateTime>();
char[] seperators = {' '};
Parallel.ForEach(GetLines(reader), line =>
{
//We only need the first field so limit the split to 2
string[] fields = line.Split(seperators, 2);
results.Enqueue(DateTime.ParseExact(fields[0], "yyyyMMddTHHmmssfff", ci));
});
return results
}
私有静态IEnumerable GetLines(文本阅读器)
{
而(!reader.EndOfStream)
{
产生返回reader.ReadLine();
}
}
私有静态CultureInfo ci=CultureInfo.InvariantCulture;
公共静态ConcurrentBag ProcessData(文本阅读器)
{
ConcurrentBag结果=新ConcurrentBag();
字符[]分隔符={''};
Parallel.ForEach(GetLines(reader),line=>
{
//我们只需要第一个字段,所以将拆分限制为2
string[]字段=line.Split(分隔符,2);
results.Enqueue(DateTime.ParseExact(字段[0],“yyyyymmddthhmmssff”,ci));
});
返回结果
}
这样做的缺点是顺序松散,如果这对您很重要,那么有一个版本Parallel.ForEach可以。您需要存储带有日期的行号(可能是一个
ConcurrentDictionary
或在包中存储一个元组
),然后在稍后对数据进行排序。拆分字符串的想法是正确的,但子字符串的速度很慢。每当我拆分字符串时,我都使用字符访问器。
yyyyMMddTHHmmssfff
免责声明:T
public class DateParser1
{
private static System.String DateFormat="yyMMddTHHmmssfff";
public static System.DateTime GetDate(System.String SourceString, int Offset=0) // Offset eliminates need for substring
{
int Year=0;
int Month=0;
int Day=0;
int Hour=0;
int Minute=0;
int Second=0;
int HourOffset=0;
int MS=0;
if(SourceString.Length+Offset<DateFormat.Length) throw new System.Exception(System.String.Format("Date Too Short {0} For {0}",SourceString.Substring(Offset),DateFormat));
for(int i=0;i<DateFormat.Length;i++)
{
System.Char c=SourceString[Offset+i];
switch(DateFormat[i])
{
case 'y':
Year=Year*10+(c-'0');
break;
case 'M':
Month=Month*10+(c-'0');
break;
case 'd':
Day=Day*10+(c-'0');
break;
case 'T':
if(c=='p'||c=='P')
HourOffset=12;
break;
case 'h':
Hour=Hour*10+(c-'0');
if(Hour==12) Hour=0;
break;
case 'H':
Hour=Hour*10+(c-'0');
HourOffset=0;
break;
case 'm':
Minute=Minute*10+(c-'0');
break;
case 's':
Second=Second*10+(c-'0');
break;
case 'f':
MS=MS*10+(c-'0');
break;
}
}
if(Year>30) //Change For Your Business Rules
{
Year+=1900;
}
else
{
Year+=2000;
}
try
{
return new System.DateTime(Year,Month,Day,Hour+HourOffset,Minute,Second,MS);
}
catch(System.Exception)
{
throw new System.Exception(System.String.Format("Error In Date: {0}/{0}/{0} {0}:{0}:{0}.{0} - {0} {0}",Year,Month,Day,Hour+HourOffset,Minute,Second,MS,DateFormat,SourceString.SubString(Offset,DateFormat.Length)));
}
}
}
公共类DateParser1
{
private static System.String DateFormat=“yymddthhmmssff”;
public static System.DateTime GetDate(System.String SourceString,int Offset=0)//Offset不需要子字符串
{
整年=0;
整月=0;
整日=0;
整小时=0;
int分钟=0;
int秒=0;
int hourofset=0;
int-MS=0;
if(SourceString.Length+Offset不确定这是否会更快,但您可以将日期字符串转换为long,然后按如下方式进行算术拆分:
string dateStr = "20131108134701234"; //yyyyMMddHHmmssfff
long dateLong = long.Parse(dateStr);
int f = (int) (dateLong % 1000);
int s = (int) ((dateLong % 100000 - f) / 1000);
int mi = (int) ((dateLong % 10000000 - s - f) / 100000);
int h = (int) ((dateLong % 1000000000 - mi - s - f) / 10000000);
int d = (int) ((dateLong % 100000000000 - h - mi - s - f) / 1000000000);
int mo = (int) ((dateLong % 10000000000000 - d - h - mi - s - f) / 100000000000);
int y = (int) ((dateLong % 100000000000000000 - mo - d - h - mi - s - f) / 10000000000000);
DateTime dateDT = new DateTime(y, mo, d, h, mi, s, f);
(天真、未经优化的实现)这是用于对框架代码进行基准测试的代码(参考我的评论)。我在发布模式.Net 4.5.2、32位控制台中运行此代码
static void Main(string[] args)
{
const string date = "2015-04-11T12:45:59";
const string format = "yyyy-MM-ddTHH:mm:ss";
var reference = FrameworkParse(date, format);
var method1 = JamesBarrettParse(date, format);
if (reference != method1)
{
throw new Exception(string.Format("reference date {0} does not match SO date {1}",reference.ToString("s"),method1.ToString("s")));
}
const int iterations = 1000000;
var sw = new Stopwatch();
//FRAMEWORK PARSE
Console.Write("Starting framework parse for {0} iterations...", iterations);
sw.Start();
DateTime dt;
for (var i = 0; i < iterations; i++)
{
dt = FrameworkParse(date, format);
if (dt.Minute != 45)
{
Console.WriteLine("duh");
}
}
sw.Stop();
Console.WriteLine("DONE in {0} millis",sw.ElapsedMilliseconds.ToString("F2",CultureInfo.InvariantCulture));
//James Barrett parse
Console.Write("Starting JB parse for {0} iterations...", iterations);
sw.Restart();
for (var i = 0; i < iterations; i++)
{
dt = JamesBarrettParse(date, format);
if (dt.Minute != 45)
{
Console.WriteLine("duh");
}
}
sw.Stop();
Console.WriteLine("DONE in {0} millis",sw.ElapsedMilliseconds.ToString("F2",CultureInfo.InvariantCulture));
Console.Write("press any key to exit");
Console.ReadKey();
}
private static DateTime FrameworkParse(string s, string format, CultureInfo info = null)
{
var time = DateTime.ParseExact(s, format,
info ?? CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
return time;
}
static void Main(字符串[]args)
{
const string date=“2015-04-11T12:45:59”;
const string format=“yyyy-MM-ddTHH:MM:ss”;
var reference=FrameworkParse(日期、格式);
var method1=jamesbarrettprase(日期、格式);
如果(参考!=method1)
{
抛出新异常(string.Format(“引用日期{0}不匹配,所以日期{1}”)、reference.ToString(“s”)、method1.ToString(“s”);
}
const int迭代次数=1000000;
var sw=新秒表();
//框架解析
Write(“为{0}迭代开始框架解析…”,迭代);
sw.Start();
日期时间dt;
对于(var i=0;i
输出为
启动1000000次迭代的框架解析…在2058.00毫秒内完成
启动1000000次迭代的JB解析…在324.00毫秒内完成
按任意键退出
类似于詹姆斯·巴雷特的回答,但我想得更快,因为它更直接:
假设您有一个格式为yyMMdd的日期
我发现转换它的最快方法是:
var d = new DateTime(
(s[0] - '0') * 10 + s[1] - '0' + 2000,
(s[2] - '0') * 10 + s[3] - '0',
(s[4] - '0') * 10 + s[5] - '0')
var d = DateTime.ParseExact(s, "yyMMdd", System.Globalization.CultureInfo.InvariantCulture);