tags:

views:

785

answers:

2

Hello,

In my case I have five huge text files,which I have to embedd into one text file.

I tried with StreamReader(),but I don't know how to make it Read one more file,do I have to assign another variable?

Showing an example will be appreciated greatfully.

+7  A: 

New answer

(See explanation for junking original answer below.)

static void CopyFiles(string dest, params string[] sources)
{
    using (TextWriter writer = File.CreateText(dest))
    {
        // Somewhat arbitrary limit, but it won't go on the large object heap
        char[] buffer = new char[16 * 1024]; 
        foreach (string source in sources)
        {
            using (TextReader reader = File.OpenText(source))
            {
                int charsRead;
                while ((charsRead = reader.Read(buffer, 0, buffer.Length)) > 0)
                {
                    writer.Write(buffer, 0, charsRead);
                }
            }
        }
    }
}

This new answer is quite like Martin's approach, except:

  • It reads into a smaller buffer; 16K is going to be acceptable in almost all situations, and won't end up on the large object heap (which doesn't get compacted)
  • It reads text data instead of binary data, for two reasons:
    • The code can easily be modified to convert from one encoding to another
    • If each input file contains a byte-order mark, that will be skipped by the reader, instead of ending up with byte-order marks scattered through the output file at input file boundaries

Original answer

Martin Stettner pointed out an issue in the answer below - if the first file ends without a newline, it will still create a newline in the output file. Also, it will translate newlines into the "\r\n" even if they were previously just "\r" or "\n". Finally, it pointlessly risks using large amounts of data for long lines.

Something like:

static void CopyFiles(string dest, params string[] sources)
{
    using (TextWriter writer = File.CreateText(dest))
    {
        foreach (string source in sources)
        {
            using (TextReader reader = File.OpenText(source))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    writer.WriteLine(line);
                }
            }
        }
    }
}

Note that this reads line by line to avoid reading too much into memory at a time. You could make it simpler if you're happy to read each file completely into memory (still one at a time):

static void CopyFiles(string dest, params string[] sources)
{
    using (TextWriter writer = File.CreateText(dest))
    {
        foreach (string source in sources)
        {
            string text = File.ReadAllText(source);
            writer.Write(text);
        }
    }
}
Jon Skeet
Skeet beats me again!
jrcs3
He just took a look at his keyboard and it began typing the answer with the speed of light. :)
John
Skeet must be an android. He can't possibly be human. +1
ichiban
Please correct me if I'm wrong, but the first version will insert additional EOL characters if one file hasn't one at its end. So the two programs will not have the same behaviour imo.Also, you could theoretically run into troubles if you have really long lings (such that ReadLine isn't able to read them in the internal buffer). I think a version using an preallocated buffer and Stream.Read/Stream.Write might be more robust.
MartinStettner
Ooh yes, you're right about the first one. I don't agree about using a stream directly though. Will post an updated version.
Jon Skeet
Oh, and the ReadLine issue would only be a problem if a single line was enough to exceed not just an internal buffer, but your system memory. Still, it is a pointless limit.
Jon Skeet
Sure, that's why I wrote "theoretically" :-))) On the other hand, if you work on really big files, you might also run into performance troubles because of the additional encoding work done by StreamReader. A version using FileStreams might help at least in these cases (I admit that the TextWriter-based solution will be better in most circumstances)
MartinStettner
I'd expect the IO overhead to overwhelm the computational overhead - and with prefetch, hopefully those two *will* occur in parallel. But yes, it's certainly doing more work.
Jon Skeet
+2  A: 

Edit:

As Jon Skeet pointed out, text files usually should be handled differently than binary files .

I just leave this answer since it might be more performant if you have really big files and aren't concernded by encoding issues (such as different input files having different encodings or multiple Byte Order Marks in the output file):

public void CopyFiles(string destPath, string[] sourcePaths) {
  byte[] buffer = new byte[10 * 1024 * 1024]; // Just allocate a buffer as big as you can afford
  using (var destStream= = new FileStream(destPath, FileMode.Create) {
    foreach (var sourcePath in sourcePaths) {
      int read;
      using (var sourceStream = FileStream.Create(sourcePath, FileMode.Open) {
        while ((read = sourceStream.Read(buffer, 0, 10*1024*1024)) != 0)
          destStream.Write(buffer, 0, read);
      }
    }
  }
}
MartinStettner
It does make a difference - consider what happens if all the text files start with a byte order mark. You would want your output to only have a single one.
Jon Skeet
Thank you for pointing this out. Interestingly, MSDN doesn't mention that File.OpenText() (or even StreamReader) consumes the byte order marks. BOMs are not even mentioned in all StreamReader-constructors documentation. Moreover MSDN states that File.OpenText works with UTF-8 files while it really uses the same detection mechanism as StreamReader (thus perfectly working with any other supported encoding).
MartinStettner
I'd take issue with "any other supported encoding" - it's "any auto-detected encoding" which is a very different thing IMO :)
Jon Skeet