This is a typical indexing problem.
Let me start with saying there are better systems for this: storing your data in a database would make this problem real easy.
But, just for fun, Ill give it a try.
Since your date structure is ordered, alphabetically, if you find any valid point within the set (date range), you have found the set and you can proceed.
A typical way to accomplish:
- start at half point
- if you're not in the target set, go half of the half up or down, depending on the value you have hit. (if it's too big, go up, if it's too small, go down)
- repeat until you hit the set; from there you can loop both way's to get the full set (the target range)
As @Cid pointed out, this will only work correctly if you have an index of some sort.
You can accomplish this (depending on how often you need to run the query) by creating an index upfront, either in memory or on disk.
If it is a 1 time task, you can still do this binary search, but not on the index but on the file bytes, by using Stream.Seek, it will not give an optimal result, but it will on average still be faster than going through the whole file.
Do note: it will be hard to get it right. It might be best to close in into an actual data point and start reading if the distance is within a specific range.
If your stream is not Seekable (like a network stream or such), and it's a one time query, you will need to iterate over the whole file and optimization options are minimal.
Example
Disclaimer
Code will contain bugs and is not suited for production.
3 issues:
- It's not suited for variable bit encoding (due to the seeking)
- It's will not find the first index yet (trying to work on it)
- Not sure how it behaves if the index is not there, most likely a stackoverflowexception.
- its not suited for a range yet, I think I read the question wrong XD
Consist of 3 functions: 1 main to call it, a recursive search and a "starter".
Used a 10MB log file, which actually contains a lot of errors due to an issue.
The log:
2020-11-08 23:10:06,201 [24] ERROR Unity.SelfHostWebApiOwin.UnityHelpers [(null)] - Error while requesting stats.
System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
at ShowStatsService.LogSource() in ShowStatsService.cs:line 109
LogSource
2020-11-08 23:10:16,209 [32] ERROR Unity.SelfHostWebApiOwin.UnityHelpers [(null)] - Error while requesting stats.
System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
at ShowStatsService.LogSource() in ShowStatsService.cs:line 109
LogSource
First the main:
static void Main(string[] args)
{
using FileStream bigFile = new FileStream(".\\testdata\\log.txt", FileMode.Open);
var indexToFind = "2020-11-10 01:31";
//searches for YYYY-MM-DD HH:MM date/time format
var regexPatternLocator = @"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}";
Func<string, bool> comparer = rawInput => new Regex(regexPatternLocator).IsMatch(rawInput);
var result = FindByIndex(bigFile,comparer,
indexValue => string.Compare(indexValue.Substring(0,indexToFind.Length), indexToFind));
Console.WriteLine($"Result: {result}");
}
The starter:
/// <summary>
/// Retrieves a value by index, including index
/// </summary>
/// <param name="seekableStream">Target text based stream</param>
/// <param name="indexLocator">Expression to identify an index</param>
/// <param name="compareFunction">Expression to compare an index</param>
/// <returns></returns>
public static string FindByIndex(Stream seekableStream, Func<string, bool> indexLocator,
Func<string, int> compareFunction)
{
return FindByNonUniqueIndexInternal(seekableStream, indexLocator,
compareFunction,
seekableStream.Length / 2, 0,0,0);
}
The searcher:
/// <summary>
/// Some additional parameters t keep track of stats & counts
/// </summary>
/// <returns></returns>
private static string FindByNonUniqueIndexInternal(Stream seekableStream, Func<string, bool> indexLocator,
Func<string, int> compareFunction, long position, long previousPosition, int nIteration, int nReadedLines)
{
Console.WriteLine($"Attempts: {nIteration}");
Console.WriteLine($"Lines Read: {nReadedLines}");
Console.WriteLine($"Position: {position}");
nIteration++;
seekableStream.Seek(position, SeekOrigin.Begin);
//using using declaration, see https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using
//Note: if using variable bit UTF encoding, this is going to be harder, with
//subtle issues relating to the fact that a char can have various byte widths
using var reader = new StreamReader(seekableStream,Encoding.ASCII);
string line;
while ((line = reader.ReadLine()) != null)
{
position+= line.Length;
nReadedLines++;
if (indexLocator(line)) //index type found
{
var indexComparerValue = compareFunction(line);
var length = Math.Abs(position - previousPosition);
var jump = length / 2;
if (indexComparerValue < 0) //found index value is to big
{
//recursive call
return FindByNonUniqueIndexInternal(seekableStream,
indexLocator,
compareFunction,
position + jump,
position,
nIteration,
nReadedLines);
}
if (indexComparerValue > 0) //found index value is to small
{
//recursive call
return FindByNonUniqueIndexInternal(seekableStream,
indexLocator,
compareFunction,
position - jump,
position,
nIteration,
nReadedLines);
}
else
{
//reverse find
//jump back
// needs to be implemented
var sb = new StringBuilder(line);
//read until end or next index
while ((line = reader.ReadLine()) != null)
{
if (indexLocator(line) && compareFunction(line) != 0)
break; //hit a new index, so break.
sb.AppendLine(line);
}
return sb.ToString();
}
}
}
return string.Empty;
}
The output (removed sensitive data):
Attempts: 8
Lines Read: 50
Position: 5943268
Result: 2020-11-10 01:31:21,045 [38] ERROR Unity.SelfHostWebApiOwin.UnityHelpers [(null)] - Error while requesting stats.System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
2020-11-10 01:31:31,057 [42] ERROR Unity.SelfHostWebApiOwin.UnityHelpers [(null)] - Error while requesting stats.
System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
2020-11-10 01:31:41,063 [38] ERROR Unity.SelfHostWebApiOwin.UnityHelpers [(null)] - Error while requesting stats.
System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
2020-11-10 01:31:51,064 [29] ERROR Unity.SelfHostWebApiOwin.UnityHelpers [(null)] - Error while requesting stats.
System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
at ShowStatsService.LogSource in ShowStatsService.cs:line 109
ShowStatsService.LogSource