Сохранить определенную часть огромного текстового файла (более 2 ГБ) - PullRequest
4 голосов
/ 14 декабря 2011

У меня есть большие файлы журнала, которые содержат временные метки каждую секунду. Мне нужно вырезать определенную пользователем часть из этого огромного файла и сохранить ее в другом текстовом файле. Я запутался, так как класс fstream может работать с maxразмер файла 2 ГБ, чтение всех строк - это проблема времени и памяти.

шаблон метки времени:! << дд.мм.гггг чч: мин: сек> каждую секунду и по одному в строке.один проф.парень предложил использовать 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 

и так далее до EOF.

Ответы [ 2 ]

3 голосов
/ 14 декабря 2011

Начните с обоснованного предположения о том, как глубоко отметка времени находится в файле.Если вы не можете этого сделать, начните с середины - по сути, выполните бинарный поиск.

Как только вы попытаетесь найти нужное место, прочитайте несколько строк (*), пока не найдете отметку времени.В этот момент у вас либо есть временная метка, либо вы можете определить, находится ли она до или после текущей точки.Если это не ваша временная метка, ищите назад или вперед логическую величину и повторяйте ее, пока не найдете нужную временную метку.

С помощью этого метода вы, вероятно, сможете найти свою временную метку всего с несколькими дюжинами операций чтения или около того.

Возможно, вы захотите прочитать на поиск по MSDN .

*, имейте в виду, что при поиске указатель файла может не находиться в начале строки.Конечно, эта техника все еще будет работать, но об этом следует помнить, когда вы сузили область поиска до очень небольшого диапазона.

3 голосов
/ 14 декабря 2011

ReadLine совсем не то, что вы хотите сделать ... открыть средство чтения файлов ... искать нужную позицию, считывать нужные данные (в другой поток файлов).

«ReadLine» должен фактически прочитать данные ... тогда как поиск (myStream.Position = whereIWantToGo) в основном мгновенный.

Вы бы справились с этим так же, как с отсортированной базой данных. БД с 1 000 000 записей занимает всего 20 операций «поиска», чтобы найти ... начать на полпути, слишком высоко? только что спас 500 000 ищет ... вернуться на полпути ... слишком высоко? просто сбрил еще 250 000 поисков ... промыть, повторить.

Если вы найдете забавные символы (плохая кодировка)

По электронной почте (между прочим, вы должны продолжать использовать S.O., а не электронную почту, чтобы другие люди могли извлечь выгоду) ... Ответ заключается в том, что вам нужно попробовать разные типы кодировки. Ваш файл может быть не в кодировке UTF8 (это то, что ожидает мой код ниже). Поэтому используйте new StreamReader("MyLogFile.txt", Encoding.ASCII) или другую кодировку, пока она не будет работать для вас.

Консольное приложение C #, которое должно помочь вам начать работу

Отказ от ответственности ... этот код неприятен и может содержать ошибки, когда есть бесконечный цикл:) ... но вот консольное приложение, которое должно работать для вас.

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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...