69

I am using a list to limit the file size since the target is limited in disk and ram. This is what I am doing now but is there a more efficient way?

readonly List<string> LogList = new List<string>();
...
var logFile = File.ReadAllLines(LOG_PATH);
foreach (var s in logFile) LogList.Add(s);
jacknad
  • 13,483
  • 40
  • 124
  • 194
  • Isn't it going to be rather hard to add entries to `LogList` when you've marked it as `readonly`? – Tim Aug 01 '11 at 21:02
  • 7
    That means you can't reassign the reference rather than it's a readonly list. – Deleted Aug 01 '11 at 21:03
  • Please don't put "C#" in your title just to say what the question is about - that's what the tags are for. – John Saunders Aug 01 '11 at 21:04
  • readonly usually means that is can be written only within the class it is declared in. That is it cannot be changed from outside. – ScruffyDuck Aug 01 '11 at 21:04
  • 1
    @JackN: how do you use LIST to limit FILE size, can you explain? – Tigran Aug 01 '11 at 21:05
  • @ScruffyDuck: readonly means that it can be assigned only once. – Tigran Aug 01 '11 at 21:05
  • From MSDN: "The readonly keyword is a modifier that you can use on fields. When a field declaration includes a readonly modifier, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class." http://msdn.microsoft.com/en-us/library/acdd6hb7(v=VS.100).aspx (in case anyone else was wondering) – Tim Aug 01 '11 at 21:07
  • 2
    @Tim: adding items to a list is done through the `Add` method. As with any other object, the list is assigned to a field **once**, but that doesn't stop you from modifying its own properties and fields. – vgru Aug 01 '11 at 21:14
  • 2
    @Tim - for reference types, that only means you can't change the reference itself. It doesn't mean you can't modify members of the object referred to. – Joel Coehoorn Aug 01 '11 at 21:15
  • @Tigran: I limit the size this way before saving it back to a the disk file: while (LogList.Count > LOG_MAX) LogList.RemoveAt(0); – jacknad Aug 01 '11 at 21:19
  • Groo and Joel Coehoorn - thanks for the additional clarification :) – Tim Aug 01 '11 at 21:20
  • @JackN: so, you are doing this to trim the log file? In that case you really don't need to load the entire file in memory. – vgru Aug 01 '11 at 21:22
  • @Groo: Is there a better way to remove lines from the beginning of a file? If so, then I don't even need the list. – jacknad Aug 01 '11 at 21:24
  • @JackN: I've updated my answer with one way of doing it. However, if you don't load it in memory, you will need enough disk space to have **both source and target file** at once. – vgru Aug 01 '11 at 21:29

10 Answers10

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

Since logFile is an array, you can pass it to the List<T> constructor. This eliminates unnecessary overhead when iterating over the array, or using other IO classes.

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;
            }
        }   
        ...
} 
Evan Mulawski
  • 54,662
  • 15
  • 117
  • 144
71

A little update to Evan Mulawski answer to make it shorter

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

Roberto
  • 11,557
  • 16
  • 54
  • 68
Ram
  • 15,908
  • 4
  • 48
  • 41
14

Why not use a generator instead?

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

Then you can use it like you would use the list:

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

Of course, if you need to have a List<string>, then you will need to keep the entire file contents in memory. There's really no way around that.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • It should be noted that `ReadLines` does what you are doing here. `foreach (string line in File.ReadLines(@"d:\data\episodes.txt"))` https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlines – HackSlash Aug 20 '20 at 21:55
11

You can simple read this way .

List<string> lines = System.IO.File.ReadLines(completePath).ToList();
AmmarTheTrainer
  • 153
  • 1
  • 9
6

[Edit]

If you are doing this to trim the beginning of a log file, you can avoid loading the entire file by doing something like this:

// 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);
}

First of all, since File.ReadAllLines loads the entire file into a string array (string[]), copying to a list is redundant.

Second, you must understand that a List is implemented using a dynamic array under the hood. This means that CLR will need to allocate and copy several arrays until it can accommodate the entire file. Since the file is already on disk, you might consider trading speed for memory and working on disk data directly, or processing it in smaller chunks.

  1. If you need to load it entirely in memory, at least try to leave in an array:

     string[] lines = File.ReadAllLines("file.txt");
    
  2. If it really needs to be a List, load lines one by one:

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

    Note: List<T> has a constructor which accepts a capacity parameter. If you know the number of lines in advance, you can prevent multiple allocations by preallocating the array in advance:

     List<string> lines = new List<string>(NUMBER_OF_LINES);
    
  3. Even better, avoid storing the entire file in memory and process it "on the fly":

     using (var sr = new StreamReader("file.txt"))
     {
          string line;
          while ((line = sr.ReadLine()) != null) 
          {
              // process the file line by line
          }
     }
    
Mark Glorie
  • 3,733
  • 3
  • 28
  • 31
vgru
  • 49,838
  • 16
  • 120
  • 201
5

Don't store it if possible. Just read through it if you are memory constrained. You can use a StreamReader:

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

This can be wrapped in a method which yields strings per line read if you want to use LINQ.

Deleted
  • 4,804
  • 1
  • 22
  • 17
4
//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
List<string> lines = new List<string>();
 using (var sr = new StreamReader("file.txt"))
 {
      while (sr.Peek() >= 0)
          lines.Add(sr.ReadLine());
 }

i would suggest this... of Groo's answer.

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

I also use the Environment.NewLine.toCharArray as well, but found that didn't work on a couple files that did end in \r\n. Try either one and I hope it works well for you.

vSteve
  • 63
  • 1
  • 1
  • 8
  • Welcome to Stack Overflow! Code-only answers are discouraged here. Can you add some explanation as to how this best answers the question? – Nate Barbettini Sep 19 '15 at 03:29
0
string inLine = reader.ReadToEnd();
myList = inLine.Split(new string[] { "\r\n" }, StringSplitOptions.None).ToList();

This answer misses the original point, which was that they were getting an OutOfMemory error. If you proceed with the above version, you are sure to hit it if your system does not have the appropriate CONTIGUOUS available ram to load the file.

You simply must break it into parts, and either store as List or String[] either way.

Prasad Telkikar
  • 15,207
  • 5
  • 21
  • 44
RTZ ZZ
  • 11
  • 1