保存一个巨大文本文件的特定部分(超过2GB)
本文关键字:定部 超过 2GB 文本 一个 巨大 保存 文件 | 更新日期: 2023-09-27 18:18:03
我有一个大的日志文件,其中包含每秒钟的时间戳。我需要的是从这个巨大的文件中剪切一个用户定义的部分,并将其保存在另一个文本文件中。我很困惑,因为fstream类可以处理最大2GB的文件大小,读取所有行是时间和内存灾难。
时间戳:<<Dd.mm.yyyy hh:min:sec>每秒一次,每行一次。一位教授建议使用LINQ和readline()。
文件的示例:
!<<14.12.2012 16:20:03>
some text some text some
some text some text some
some text some text some
!<<14.12.2012 16:20:04>
some text some text some
some text some text some
some text some text some
some text some text some
some text some text some
!<<14.12.2012 16:20:05>
some text some text some
!<<14.12.2012 16:20:06>
some text some text some
some text some text some
ReadLine根本不是你想做的…打开文件阅读器…查找到你想要的位置,读出你想要的数据(到另一个文件流)。
"ReadLine"必须实际读取数据…而寻找(myStream)。Position = whereIWantToGo)基本上是即时的。
您将以与排序数据库相同的方式处理此问题。拥有1,000,000条记录的DB只需要20次"seek"操作即可找到…半途起步,太高了?刚刚节省了50万份……半路回来……太高了吗?刚刚又裁掉了25万名求职者……冲洗,重复。
如果你发现有趣的字符(编码错误)
根据你的电子邮件(顺便说一句,你真的应该继续使用S.O,而不是电子邮件——这样其他人就可以受益)…答案是,您需要尝试不同的编码类型。您的文件可能没有编码UTF8(这是我下面的代码所期望的)。因此,请使用new StreamReader("MyLogFile.txt", Encoding.ASCII)
或其他编码,直到它适合您为止。
c#控制台应用程序,应该让你开始
免责声明…这段代码很讨厌,并且可能有无限循环的bug:)…但是,这里有一个控制台应用程序应该为你工作。
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// example dates
var lookFor = new DateTime(2012, 12, 14, 16, 20, 02);
var readUntilDate = new DateTime(2012, 12, 14, 16, 20, 05);
using (var stream = File.OpenText("MyLogFile.txt"))
{
if (SeekToEntry(stream, lookFor) == false)
{
Console.WriteLine("Could not find entry for date {0}", lookFor);
return;
}
foreach (var line in ReadEntriesUntil(stream, readUntilDate))
{
Console.WriteLine("Line: {0}", line);
}
}
}
// This method simply spits out one line at a time until it hits
// the target cut-off.
static IEnumerable<string> ReadEntriesUntil(StreamReader stream, DateTime target)
{
while (true)
{
string line = stream.ReadLine();
if (line == null)
{
break;
}
if (line.StartsWith("!<<"))
{
DateTime entryDate;
if (DateTime.TryParseExact(line.Substring(3, 19).Replace(".", ""), @"ddMMyyyy HH:mm:ss",
CultureInfo.InvariantCulture, DateTimeStyles.None, out entryDate))
{
if (entryDate >= target)
{
break;
}
}
}
yield return line;
}
}
// This method will bounce around the stream till it finds your
// target entry date.
static bool SeekToEntry(StreamReader stream, DateTime target)
{
long from = 0;
long to = stream.BaseStream.Length;
while (true)
{
long testIndex = (to - from) / 2;
stream.BaseStream.Seek(testIndex, SeekOrigin.Begin);
var entryDate = GetNextEntryDate(stream, out testIndex);
if (entryDate == null || (from == to))
{
return false;
}
switch (entryDate.Value.CompareTo(target))
{
case -1:
// Found too low...
from = testIndex;
break;
case 1:
// Fount too high...
to = testIndex;
break;
default: return true;
}
}
}
// This is a function that is meant to keep seeking forward until
// it hits an entry date.
static DateTime? GetNextEntryDate(StreamReader stream, out long actualIndex)
{
actualIndex = stream.BaseStream.Position;
DateTime? result = null;
string line = null;
// Find the next entry.
while ((line = stream.ReadLine()) != null && line.StartsWith("!<<") == false) ;
if (line != null)
{
actualIndex = stream.BaseStream.Position - line.Length;
DateTime timeStamp;
if (DateTime.TryParseExact(line.Substring(3, 19).Replace(".", ""), @"ddMMyyyy HH:mm:ss",
CultureInfo.InvariantCulture, DateTimeStyles.None, out timeStamp))
{
result = timeStamp;
}
}
return result;
}
}
}
首先对时间戳在文件中的深度进行有根据的猜测。如果你不能做到这一点,从中间开始——实际上,做一个二分搜索。
找到一个点后,读取几行(*),直到找到时间戳。此时,您可以得到时间戳,也可以确定它是在当前点之前还是之后。如果它不是你的时间戳,向后或向前查找一个逻辑量,并重复,直到找到你要找的时间戳。
使用这种技术,您可能只需要几十次读取就可以找到时间戳。
你可能需要阅读一下MSDN上的seek
*请注意,当您查找时,文件指针可能不在一行的开头。当然,这种方法仍然有效,但是当您将搜索范围缩小到非常小的范围时,需要注意这一点。