2011-08-01 11 views
35

Używam listy, aby ograniczyć rozmiar pliku, ponieważ cel jest ograniczony na dysku i pamięci RAM. To właśnie teraz robię, ale czy istnieje skuteczniejszy sposób?Jak najlepiej odczytać plik z listy <string>

readonly List<string> LogList = new List<string>(); 
... 
var logFile = File.ReadAllLines(LOG_PATH); 
foreach (var s in logFile) LogList.Add(s); 
+0

Czy nie będzie raczej trudno dodawać wpisy do 'LogList', gdy zaznaczysz je jako' readonly'? – Tim

+4

Oznacza to, że nie można ponownie przypisać odwołania, a nie jest to lista tylko do odczytu. – Deleted

+0

Proszę nie umieszczać w tytule "C#" tylko po to, by powiedzieć, o co chodzi w tym pytaniu - po to są te znaczniki. –

Odpowiedz

75
var logFile = File.ReadAllLines(LOG_PATH); 
var logList = new List<string>(logFile); 

Od logFile jest tablicą, można przekazać do konstruktora List<T>. Eliminuje to niepotrzebny narzut podczas iteracji na macierzy lub przy użyciu innych klas IO.

Actual constructor implementation:

public List(IEnumerable<T> collection) 
{ 
     ... 
     ICollection<T> c = collection as ICollection<T>; 
     if(c != null) { 
      int count = c.Count; 
      if (count == 0) 
      { 
       _items = _emptyArray; 
      } 
      else { 
       _items = new T[count]; 
       c.CopyTo(_items, 0); 
       _size = count; 
      } 
     } 
     ... 
} 
+0

Dzięki za milion. Pomyślałem, że musi to być właściwy sposób. – jacknad

5

Nie przechowuj go, jeśli to możliwe. Po prostu przeczytaj, jeśli masz ograniczoną pamięć. Można użyć StreamReader:

using (var reader = new StreamReader("file.txt")) 
{ 
    var line = reader.ReadLine(); 
    // process line here 
} 

To może być zapakowane w sposób, który plony sznurki w linii przeczytać, jeśli chcesz używać LINQ.

4

[Edytuj]

Jeśli robisz to przycięcie początku pliku dziennika, można uniknąć załadowanie całego pliku robiąc coś takiego:

// count the number of lines in the file 
int count = 0; 
using (var sr = new StreamReader("file.txt")) 
{ 
    while (sr.ReadLine() != null) 
     count++; 
} 

// skip first (LOG_MAX - count) lines 
count = LOG_MAX - count; 
using (var sr = new StreamReader("file.txt")) 
using (var sw = new StreamWriter("output.txt")) 
{ 
    // skip several lines 
    while (count > 0 && sr.ReadLine() != null) 
     count--; 

    // continue copying 
    string line = ""; 
    while (line = sr.ReadLine() != null) 
     sw.WriteLine(line); 
} 

Przede wszystkim, skoro File.ReadAllLines ładuje cały plik do tablicy łańcuchowej (string[]), kopiowanie do listy jest zbędne.

Po drugie, musisz zrozumieć, że List jest zaimplementowany przy użyciu dynamicznej macierzy pod maską. Oznacza to, że CLR będzie musiał przydzielić i skopiować kilka tablic, dopóki nie będzie w stanie pomieścić całego pliku. Ponieważ plik jest już na dysku, możesz wziąć pod uwagę szybkość transakcji dla pamięci i pracę z danymi na dysku lub przetwarzać je w mniejszych porcjach.

  1. Jeśli trzeba załadować go w całości w pamięci przynajmniej spróbować zostawić w tablicy:

    string[] lines = File.ReadAllLines("file.txt"); 
    
  2. Jeśli to naprawdę musi być List, linie ładunkowe jeden po drugim:

    List<string> lines = new List<string>(); 
    using (var sr = new StreamReader("file.txt")) 
    { 
         while (sr.Peek() >= 0) 
          lines.Add(sr.ReadLine()); 
    } 
    

    Uwaga:List<T> konstruktor która przyjmuje parametr przepustowości. Jeśli znasz numer linii z wyprzedzeniem, można zapobiec wielu przydziałów przez preallocating tablicę z góry:

    List<string> lines = new List<string>(NUMBER_OF_LINES); 
    
  3. Nawet lepiej unikać przechowywania całego pliku w pamięci i przetwarza je „w locie”:

    using (var sr = new StreamReader("file.txt")) 
    { 
         string line; 
         while (line = sr.ReadLine() != null) 
         { 
          // process the file line by line 
         } 
    } 
    
12

Dlaczego zamiast tego użyć generatora?

private IEnumerable<string> ReadLogLines(string logPath) { 
    using(StreamReader reader = File.OpenText(logPath)) { 
     string line = ""; 
     while((line = reader.ReadLine()) != null) { 
      yield return line; 
     } 
    } 
} 

Wtedy można go używać jak chcesz korzystać z listy:

var logFile = ReadLogLines(LOG_PATH); 
foreach(var s in logFile) { 
    // Do whatever you need 
} 

oczywiście, jeśli chcesz mieć List<string>, wtedy trzeba zachować całą zawartość plików w pamięci. Naprawdę nie da się tego obejść.

1
List<string> lines = new List<string>(); 
using (var sr = new StreamReader("file.txt")) 
{ 
     while (sr.Peek() >= 0) 
      lines.Add(sr.ReadLine()); 
} 

Proponuję to ... odpowiedź Groo.

29

Trochę aktualizacja Evan Mulawski odpowiedź, aby krótsze

List<string> allLinesText = File.ReadAllLines(fileName).ToList()

+0

masz na myśli evanBHOPS? – napi15

+0

Używane to. +1 !! –

2
//this is only good in .NET 4 
//read your file: 
List<string> ReadFile = File.ReadAllLines(@"C:\TEMP\FILE.TXT").ToList(); 

//manipulate data here 
foreach(string line in ReadFile) 
{ 
    //do something here 
} 

//write back to your file: 
File.WriteAllLines(@"C:\TEMP\FILE2.TXT", ReadFile); 
+1

ReadFile nie powinien zaczynać się od kapitału – Chrotenise

0
string inLine = reader.ReadToEnd(); 
myList = inLine.Split(new string[] { "\r\n" }, StringSplitOptions.None).ToList(); 

Używam również Environment.NewLine.toCharArray jak dobrze, ale okazało się, że nie działa na kilka plików, które zakończyły się \ r \ n. Wypróbuj jedną i mam nadzieję, że działa dobrze dla ciebie.

+0

Witamy w Stack Overflow! W tym miejscu odradzane są tylko odpowiedzi dotyczące kodu. Czy możesz dodać wyjaśnienie, jak najlepiej odpowiedzieć na to pytanie? –