2013-05-01 15 views
5

Chcę okresowo odczytywać plik dziennika, do którego również jest zapisywany. Program będzie okresowo odczytywał zawartość pliku dziennika i parsował go, aby wyodrębnić niektóre wartości. Ale nie chcę czytać całego pliku za każdym razem.C# - Najbardziej efektywny sposób okresowego odczytywania ostatniej części pliku

Czy istnieje sposób, aby odczytać plik z konkretnej linii?

Na przykład przy pierwszym odczycie plik ma 100 wierszy. Zanotowałem tę wartość i następnym razem, gdy przeczytam, zaczynam czytać od linii 100 i zapisuję numer linii bieżącego pliku.

Czy jest to skuteczny sposób? Plik dziennika wzrośnie do około 100 MB i muszę czytać co 5 sekund. Więc nie byłoby tak wydajne, aby za każdym razem przeczytać kompletny plik.

Wszelkie sugestie są mile widziane.

+2

Można spojrzeć na Tail.NET http://www.codeproject.com/Articles/7568/Tail-NET – itsmatt

+0

Można podzielić plik na kilka plików lub DB przeznaczony do tego celu. Chociaż nie jestem pewien, jaką korzyść zapewnia tylko 100 MB pliku. Strumienie mają zazwyczaj indeksy początkowe. Dlaczego po prostu tego nie użyć? –

Odpowiedz

3

Myślę, że szukasz tego, gdzie offset będzie zależał od tego, jak bardzo chcesz wrócić. Numer referencyjny: MSDN

using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read)) 
{ 
    fs.Seek(offset, SeekOrigin.End); 
} 

Teraz FileStream wskazuje jednak daleko do tyłu w pliku można ustawić „przesunięcie” być, i można odczytać stamtąd.

+0

... hah, jestem o dwie minuty za wolny;) –

+0

@bland Dziękuję. Spojrzę na tę metodę. Wygląda na to, że to może dla mnie zadziałać. – madu

2

Jeśli dziennik jest tylko dołączony, można spróbować otworzyć plik w trybie tylko do odczytu bez blokady. W ten sposób inne procesy mogą do niego pisać podczas czytania.

var fs = new FileStream(path,FileMode.Open,FileAccess.Read, FileShare.ReadWrite); 
+0

Jeśli pamiętasz długość pliku, możesz mieć opcję "Szukaj" w tej lokalizacji przy następnym odczycie. – JosephHirn

1

Za coś szybkiego i brudnego, używam tego. W tym przypadku jest to zrzut log - I naprawdę nie obchodzi, ile wierszy mam, po prostu chcę pęczek na końcu (numBytes):

cmdLogReader = new System.IO.StreamReader(cmdLogFileIn); 

if (cmdLogReader.BaseStream.Length < (numBytes - 1)) { 
    return cmdLogReader.ReadToEnd; 
} else { 
    cmdLogReader.BaseStream.Seek(-numBytes, System.IO.SeekOrigin.End); 
    cmdLogReader.ReadLine(); 
    return cmdLogReader.ReadToEnd;   
} 

Zawsze możesz zapisać BaseStream.Length na początku i użyj tego, aby obliczyć, jak daleko do powrotu następnym razem (np. numBytes stanie się BaseStream.Length - previousBaseStreamLength lub cokolwiek innego), które pozwoli połączeń sekwencyjnych chwycić to, co zostało dodane od czasu ostatniego odczytu.

Być może będziesz musiał pominąć wywołanie ReadLine, jeśli zrobisz to, ponieważ tak naprawdę jest tam, aby przejść do najbliższej linii po wycofaniu losowej kwoty. Jeśli wiesz, że masz zamiar wylądować na granicy linii, możesz po prostu ReadToEnd.

Jest to trochę groteskowa implementacja, ale jest bardzo szybka, dlatego ją używam.

+0

Dziękuję. Nie wiedziałem o właściwości BaseStream.Length i za pomocą tej metody odczytałem plik. Zajrzę do tej metody. – madu

1

Seek może to zrobić dobrze. Ale chcę zapewnić inny, daleki sposób.

public static void Read() 
    { 
     var fs = new FileStream(@"G:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 
     int lastReadCount = 0; 
     while (true) 
     { 
      var totalCountOfFile = fs.Length; 
      if (lastReadCount < totalCountOfFile) 
      { 
       var buffer = new byte[1024]; 
       int count = fs.Read(buffer, 0, buffer.Length); 
       lastReadCount += count; 
       Display(buffer); 
      } 
      Thread.Sleep(5000); 
     } 
    } 

    private static void Display(byte[] buffer) 
    { 
     var text = Encoding.UTF8.GetString(buffer.Where(p=>p != 0).ToArray()); 
     Console.Write(text); 
    } 
Powiązane problemy