1

I'm trying to solve this question at the moment:

Write a program that replaces every occurrence of the substring "start" with "finish" in a text file. Can you rewrite the program to replace whole words only? Does the program work for large files (e.g. 800 MB)?

I've been trying to do it but apparently you cant read and write at the same time. If someone could look at my code and help me it would be awesome. It's throwing an exception:

The process cannot access the file 'C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt' because it is being used by another process.

You dont have to give me the answer straight but rather tell me the process. Thanks!

Here's my code at the moment

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Chapter_15_Question_7
{
class Program
{
    static void Main(string[] args)
    {
        StreamReader reader = new StreamReader(
            @"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt");

        StreamWriter writer = new StreamWriter(
            @"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt");
        using (writer)
        {
            using (reader)
            {
                string line = reader.ReadLine();
                while (line != null)
                {
                    line.Replace("start", "finish");
                    writer.WriteLine(line);
                    line = reader.ReadLine();
                }
            }
        }
    }
}

}

  • You are not reading the whole file at once, you are reading it line by line. This is reading it at once: http://stackoverflow.com/a/13509665/169714 for large files, consider using a `BufferedStream` http://stackoverflow.com/a/9643111/169714 – JP Hellemons Apr 21 '16 at 11:49
  • You could also use [File.ReadAllLines](https://msdn.microsoft.com/en-us/library/s2tte0y1(v=vs.110).aspx). – Drew Kennedy Apr 21 '16 at 11:50
  • that the point, I'm not trying to read the whole file at one go. That's the challenge. I've read reading the whole file at one go can cause an exception if the files big enough. I'm trying to find a different moment atm –  Apr 21 '16 at 11:52
  • 1
    I like how this is 100% a homework question (`Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt`). Nevertheless - `BufferesStream` is what you want. Listen to @JP Hellemons – Kenny Thompson Apr 21 '16 at 11:55
  • @KennyThompson haha you caught me. Im actually teaching myself c# using the free ebook that can be found on http://www.introprogramming.info/english-intro-csharp-book/ It comes with questions to practice the concepts at the end of every chapter :D –  Apr 21 '16 at 11:56
  • Good on you man - I reread the question and I would say, also listen to @dasblinkenlight to get around the read/write problem. – Kenny Thompson Apr 21 '16 at 11:58

4 Answers4

5

I've been trying to do it but apparently you cant read and write at the same time.

The trick to solving this problem is straightforward:

  • Open a temporary file in the same folder as the original for writing,
  • Read the original line-by-line, do the replacements, and write them to the temporary file
  • Close the original file and the temporary file
  • Copy temporary file in place of the original file

You are already reading your file line-by-line, so all you need to do is changing the writer to use a different file name, and adding a call to move the file after the loop is over.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • How does open a temporary file. Is it the same as just opening a new write stream? and use `File.Delete()` method on both? –  Apr 21 '16 at 11:55
  • Yes and no - you simply open a new stream, but you will only delete the first file and rename the temporary one... – Aconcagua Apr 21 '16 at 11:57
  • @Nate Use [`GetRandomFileName`](https://msdn.microsoft.com/en-us/library/system.io.path.getrandomfilename(v=vs.110).aspx) to make a name of your temp file. – Sergey Kalinichenko Apr 21 '16 at 11:58
  • So I delete the same file and rename the temporary file to the original file? so the process is somewhat like this? // open file read line by line // replace substring in line //write line to another file //repeat until end of first file // close first file //delete first file //rename second file to first file –  Apr 21 '16 at 11:58
  • @Nate Yes, that's the process. – Sergey Kalinichenko Apr 21 '16 at 12:08
2

Have not tested it. But this is from the links I posted in the comment.

What I would do is I'd make a temp file and write it line by line and afterwards replace the old text file with the new one.

Something like this:

string path = @"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt";
string pathTmp = @"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile-tmp.txt";

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {     
        string line;
        while ((line = sr.ReadLine()) != null)
        {                 
             using (StreamWriter writer = new StreamWriter(pathTmp))
             {
                 writer.WriteLine(line.Replace("start", "finish"));
             }
        }
    }
}
File.Delete(path);
File.Move(pathTmp, path);
JP Hellemons
  • 5,977
  • 11
  • 63
  • 128
  • I tried your method but it's throwing an argument exception at `var sw = new System.IO.StreamWriter(bs);` it says the streamwriter was not writable –  Apr 21 '16 at 12:06
  • That use of `BufferedStream` is really dubious, since `FileStream` is already buffered by default. The use of `BufferedStream` is woefully out-of-date. – Matthew Watson Apr 21 '16 at 12:09
  • You should delete the first part of this answer, since it is nonsense. The second part is ok. Also, get rid of the use of `BufferedStream` in the second part - that's out of date. – Matthew Watson Apr 21 '16 at 12:12
  • @MatthewWatson thank you for your feedback, answer updated. – JP Hellemons Apr 21 '16 at 12:17
0
    StreamWriter writer = new StreamWriter(
        @"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt");
    using (writer)
    {
        using (reader)
        {
            string line = reader.ReadLine();
            while (line != null)
            {
                string myNewLine=line.Replace("start", "finish");
                writer.WriteLine(myNewLine);
            }
        }
    }
}

}

0

I think that this would be a more succinct approach:

string fileToUpdate = @"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 7\Chapter 15 Question 7\TextFile.txt";
string tempFile = fileToUpdate + ".tmp";

File.WriteAllLines(tempFile, 
    File.ReadLines(fileToUpdate)
    .Select(line => line.Replace("start", "finish")));

File.Delete(fileToUpdate);
File.Move(tempFile, fileToUpdate);
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276