Writing a Text File in memory and saving it with savefiledialog
Asked Answered
D

5

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.

Dufrene answered 18/4, 2011 at 12:31 Comment(7)
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?Vtarj
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.Dufrene
I have added a new code to my answer. you can see it. it needs only adding one another line to your code.Mellifluent
your requirements added to my answer.Vtarj
@Sean87 don't really know what is the problem of wrapping the writing to text file part into a using statement?Glove
@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.Dufrene
@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.Glove
B
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());
Blayne answered 18/4, 2011 at 12:35 Comment(5)
@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?Dufrene
@Sean87 as written? in the current directory, whatever that is. You can replace that with any path you like - it was purely indicative.Blayne
@Marc and also, add the missing " at the end of your 2nd stringGlove
@Farzin - I'll correct it to path; the question shows no variable called dialog...Blayne
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!Dufrene
M
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());
Mellifluent answered 18/4, 2011 at 12:36 Comment(4)
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\nGlove
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.Blayne
@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.Mellifluent
@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.Lexielexigraphy
L
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.

Lexielexigraphy answered 18/4, 2011 at 12:43 Comment(3)
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{} statementDufrene
@Sean87: You can declare the StringWriter at whichever scope you desire / require, just be sure to dispose when you are actually done.Lexielexigraphy
+1 StringWriter is definitely the way to go here. It's much easier to use than MemoryStream.Decortication
V
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.

Vtarj answered 18/4, 2011 at 12:41 Comment(2)
@Jordell Hmm dont think this would help, what will happen to memorystream part of the code?Dufrene
Why do you need it? Just write it straight to the 'FileStream'.Vtarj
G
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.

Glove answered 18/4, 2011 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.