13

I am trying to make a text file in memory, add some lines to it and at the end save the file in a text file. I can handle the savedialog part but I dont know how to get the text file from memory. Any help and tips will be appriciated.

What I am doing so far is:

//Initialize in memory text writer
MemoryStream ms = new MemoryStream(); 
TextWriter tw = new StreamWriter(ms);

tw.WriteLine("HELLO WORLD!");
tw.WriteLine("I WANT TO SAVE THIS FILE AS A .TXT FILE!);

please note I will call tw.WriteLine() add more lines in different places so I want to save this at end of program (so this shouldent be wrapped between something like using{} )

UPDATE

StringBuilder seems to be a more reliable option for doing this! I get strange cut-outs in my text file when I do it using MemoryStream.

Thanks.

Dumbo
  • 13,555
  • 54
  • 184
  • 288
  • 1
    Why make it in memory first, will that be slow or interactive? Why not just write it to disk once you have the filename from a `FileSaveDialog`? – Jodrell Apr 18 '11 at 12:34
  • Well I have a simple program and dont want to bother with finding a temp path or file, I want this file to be saved only when the operation is complete. then a FileSaveDialog should be come and let user to save the file anywhere he wants. – Dumbo Apr 18 '11 at 12:39
  • I have added a new code to my answer. you can see it. it needs only adding one another line to your code. – Farzin Zaker Apr 18 '11 at 12:48
  • your requirements added to my answer. – Jodrell Apr 18 '11 at 12:55
  • @Sean87 don't really know what is the problem of wrapping the writing to text file part into a `using` statement? – Oscar Mederos Apr 18 '11 at 13:00
  • @Oscar Then I have too put all my methods and stuff inside a using statement it may work but I rather not use that for readability sake. – Dumbo Apr 18 '11 at 13:03
  • @Sean87 You only need the `using` statement when you're writing to the file. Take a look at my answer. Do you really need to have a `MemoryStream`? You can just have a `string` (`StringBuilder`, to be more specific) and append lines to it, and **when you decide to write it to the file** (after the user selects a output file in the `SaveFileDialog`, or when clicks on *Save* button,...), you write that `string` to the file. Then you won't have any problem with the `using`. – Oscar Mederos Apr 18 '11 at 13:10

5 Answers5

8

I think your best option here would be to write to a StringBuilder, and when done, File.WriteAllText. If the contents are large, you might consider writing directly to the file in the first place (via File.CreateText(path)), but for small-to-medium files this should be fine.

var sb = new StringBuilder();

sb.AppendLine("HELLO WORLD!");
sb.AppendLine("I WANT TO SAVE THIS FILE AS A .TXT FILE!");

File.WriteAllText(path, sb.ToString());
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • @Marc Thanks, my files are note going to be more than 1000 lines, but where this 'foo.txt' will be saved? would it be done with a save dialog this way? – Dumbo Apr 18 '11 at 12:42
  • @Sean87 as written? in the current directory, whatever that is. You can replace that with any path you like - it was purely indicative. – Marc Gravell Apr 18 '11 at 12:45
  • @Marc and also, add the missing `"` at the end of your 2nd string – Oscar Mederos Apr 18 '11 at 13:04
  • 1
    @Farzin - I'll correct it to `path`; the question shows no variable called `dialog`... – Marc Gravell Apr 18 '11 at 13:05
  • I changed my mind and I am using StringBuilder now since MemoryStreams just mix things up in my text file mainly I got cutted text!! Anyway Thanks! – Dumbo Apr 19 '11 at 09:08
2

Assume your SaveFileDialog name is "dialog"

File.WriteAllBytes(dialog.FileName, Encoding.UTF8.GetBytes("Your string"));

or

var text = "Your string";
text += "some other text";
File.WriteAllText(dialog.FileName, text);

also in your own solution you can do this :

MemoryStream ms = new MemoryStream(); 
TextWriter tw = new StreamWriter(ms);

tw.WriteLine("HELLO WORLD!");
tw.WriteLine("I WANT TO SAVE THIS FILE AS A .TXT FILE!);

// just add this
File.WriteAllBytes(dialog.FileName, ms.GetBuffer());
Farzin Zaker
  • 3,578
  • 3
  • 25
  • 35
  • your example won't work, because you are appending text to the string `text`, so what you will have at the end is all lines concatenated. Instead you should do something like `text += "some other text" + Environment.NewLine` or just use `\r\n` – Oscar Mederos Apr 18 '11 at 13:05
  • several bugs there; you aren't `using` a few `IDisposable` objects, but more importantly: you are writing the ***oversized*** backing buffer (`GetBuffer()`) without limiting to the actual length of the valid data. – Marc Gravell Apr 18 '11 at 13:07
  • @Marc He wants simply write some strings in file. there is no need to use IDisposable objects or care size of buffer. do not forget GC ! GC is designed for you and to do not care buffer in this simple situations. – Farzin Zaker Apr 18 '11 at 18:39
  • @FarzinZaker That is not what Marc means. He means _you_ are utilising disposable things _without_ disposing of them! And `GetBuffer`, to quote MSDN, _'Note that the buffer contains allocated bytes which might be unused.'_ So, _you_ are potentially writing invalid data. This is nothing to do with what the user wants, but simply your bad code. There is also a missing quote, breaking your code. – Grant Thomas Jun 11 '13 at 10:23
2

Or, something nigh-on the same as @Marc's answer, but different enough that I think it's worth putting out there as a valid solution:

using (var writer = new StringWriter())
{
    writer.WriteLine("HELLO WORLD!");
    writer.WriteLine("I WANT TO SAVE THIS FILE AS A .TXT FILE!");
    File.WriteAllLines(path, writer.GetStringBuilder().ToString());
}

Where path is a string representing a valid file system entry path, predefined by you somewhere in the application.

Grant Thomas
  • 44,454
  • 10
  • 85
  • 129
  • Disappointement The problem is I will add lines to my memorystream in different places of my program, it is no practical to wrap all my program in a using{} statement – Dumbo Apr 18 '11 at 12:48
  • 1
    @Sean87: You can declare the `StringWriter` at whichever scope you desire / require, just be sure to dispose when you are actually done. – Grant Thomas Apr 18 '11 at 12:49
  • +1 `StringWriter` is definitely the way to go here. It's much easier to use than `MemoryStream`. – Jim Mischel Apr 18 '11 at 13:42
0

Something like this.

Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = "Document"; // Default file name
dlg.DefaultExt = ".text"; // Default file extension
dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

// Show save file dialog box
Nullable<bool> result = dlg.ShowDialog();

// Process save file dialog box results
if (result == true)
{
    // Save document
    using (FileStream file = File.CreateText(dlg.FileName)
    {
        ms.WriteTo(file)
    }
}

I haven't worried about whether the file already exists but this should get you close.

You might need a ms.Seek(SeekOrgin.Begin, 0) too.

Jodrell
  • 34,946
  • 5
  • 87
  • 124
0

Another way of appending text to the end of a file could be:

if (saveFileDialog.ShowDialog() == DialogResult.OK) {
    using (var writer = new StreamWriter(saveFileDialog.Filename, true)) {
        writer.WriteLine(text);
    }
}

supposing that text is the string you need to save into your file. If you want to append new lines to that string in an easy way, you can do:

var sb = new StringBuilder();
sb.AppendLine("Line 1");
sb.AppendLine("Line 2");

and the resulting string will be sb.ToString()

If you already have a Stream object (in your example, a MemoryStream), you can do the same but replace the line:

using (var writer = new StreamWriter(saveFileDialog.Filename, true)) {

by

using (var writer = new StreamWriter(memoryStream)) {

Edit:
About wrapping the statements inside using:

Take in count that this is not a problem at all. In my first example, all you will have to do is to keep that StringBuilder object, and keep adding lines to it. Once you have what you want, just write the data into a text file.

If you are planning to write more than once to the text file, just clear the StringBuilder everytime you write, in order to not get duplicated data.

Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124