31

I am trying to save a file using DialogResult and StringBuilder. After making the text, I am calling the following code to save the file:

    if (dr == DialogResult.OK)
    {

        StreamWriter sw = new StreamWriter(saveFileDialog1.FileName);

        sw.Write(sb.ToString());
        sw.Close();
    }

I tried to add the second parameter to StreamWriter as Encoding.UTF8 but since the first argument is a string rather than a Stream, it does not compile it.

How can I convert that string to a stream to be able to pass the second parameter as Encoding?

The reason for this, is that somewhere in my text I have µ but when the file is saved it shows like μ so the µ is getting screwd!

Thanks

Dumbo
  • 13,555
  • 54
  • 184
  • 288

5 Answers5

54

Just wrap it in a FileStream.

StreamWriter sw = new StreamWriter(
    new FileStream(saveFileDialog1.FileName, FileMode.Open, FileAccess.ReadWrite),
    Encoding.UTF8
);

If you want to append, use FileMode.Append instead.

You should also call Dispose() on a try/finally block, or use a using block to dispose the object when it exceeds the using scope:

using(
    var sw = new StreamWriter(
        new FileStream(saveFileDialog1.FileName, FileMode.Open, FileAccess.ReadWrite),
        Encoding.UTF8
    )
)
{
    sw.Write(sb.ToString());
}

This will properly close and dispose the streams across all exception paths.

UPDATE:

As per JinThakur's comment below, there is a constructor overload for StreamWriter that lets you do this directly:

var sw = new StreamWriter(saveFileDialog1.FileName, false, Encoding.UTF8);

The second parameter specifies whether the StreamWriter should append to the file if it exists, rather than truncating it.

Polynomial
  • 27,674
  • 12
  • 80
  • 107
  • 1
    it works but adds an extra step. StreamWriter has already a constructor which accpets an enconding – lnu Nov 16 '11 at 12:15
  • @Inu - Yes, I'm using that constructor: `StreamWriter(Stream, Encoding)` – Polynomial Nov 16 '11 at 12:17
  • I don't think the FileStream will be disposed correctly, I think you need two nested using blocks. – Tim Abell Dec 08 '14 at 11:52
  • 2
    @TimAbell When the `StreamWriter` wrapper is disposed, it calls `Close()` on the underlying stream, which implicitly disposes the child stream. – Polynomial Dec 11 '14 at 11:37
  • ah, okay, useful. :-) – Tim Abell Dec 11 '14 at 15:29
  • you don't need to wrap for encoding .There is already a constructor for that . new System.IO.StreamWriter(filepathoutput,true, Encoding.UTF8)) . StreamWriter(String, Boolean, Encoding) Initializes a new instance of the StreamWriter class for the specified file by using the specified encoding and default buffer size. If the file exists, it can be either overwritten or appended to. If the file does not exist, this constructor creates a new file. C# public StreamWriter (string path, bool append, System.Text.Encoding encoding); – Jin Thakur Jan 06 '20 at 17:27
  • @JinThakur Ah yes, so there is! I wrote this back in 2011 so probably wasn't aware of that call. Will edit it in. – Polynomial Jan 07 '20 at 20:20
  • @Polynomial Thanks. – Jin Thakur Jan 13 '20 at 20:43
13

There is a constructor for filename, appendMode, encoding.

With a proper using block it looks like:

if (dr == DialogResult.OK)
{
    using (StreamWriter sw = new StreamWriter(saveFileDialog1.FileName, 
           false, Encoding.UTF8))
    {
      sw.Write(sb.ToString());
      //sw.Close();
    }
}
H H
  • 263,252
  • 30
  • 330
  • 514
2

There is a StreamWriter(string path, bool append, Encoding encoding) constructor - you could just explicitly specify the append flag too?

I said you ought to wrap your StreamWriter in a using too, i.e.

if (dr == DialogResult.OK)
{
    using(StreamWriter sw = new StreamWriter(saveFileDialog1.FileName, false, Encoding.UTF8)) {
        sw.Write(sb.ToString());
        sw.Close();
    }
}

although realistically this won't make any difference here. This effectively puts a try/finally around the code so that the StreamWriter will get cleaned up (it'll call sw.Dispose() even if an exception gets thrown in the meantime. (Some people will say this also means you no longer need the .Close since the Dispose will take care of that too but I prefer to have it anyway.)

Rup
  • 33,765
  • 9
  • 83
  • 112
  • Sorry I dont know what is wrapping! would be nice to see a bit of code please! – Dumbo Nov 16 '11 at 12:11
  • *"although realistically this won't make any difference here"* - this is incorrect. You may be able to open the file, but then find that the disk is full and the `sw.Write` throws an exception. There are other exceptional cases too. You should ALWAYS wrap any code that deals with `IDisposable` objects in a `using` or `try/finally`. You should also never call both `Close` and `Dispose` because it can result in a double-dispose, which will throw an `ObjectDisposedException`. – Polynomial Nov 16 '11 at 12:21
  • 1
    @Polynomial: I can understand dispose calling close but surely close shouldn't dispose of an object should it? – Chris Nov 16 '11 at 13:08
  • @Chris - The `Close()` method disposes the `FileStream`. I decompiled `FileStream.Close()` from mscorlib and got this: `public virtual void Close() { Dispose(true); GC.SuppressFinalize(this); }` Here's the full code: http://pastebin.com/n9d8TKns – Polynomial Nov 16 '11 at 13:20
  • @Polynomial I think Chris meant 'dispose' as in 'force GC'. That's what I meant by 'realistically' - it's only rare cases that will throw exceptions there (since StringBuilder.ToString() shouldn't) and in that case your app will have other problems - although I suppose that's why they're 'Exceptions'. No, [the dispose documentation](http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx) says a Dispose method must be callable multiple times without throwing an exception, and I've seen no documentation that says Close == Dispose always. – Rup Nov 16 '11 at 13:30
  • Close doesn't always mean a dispose, but in this case it clearly does. In fact, `Dispose()` calls `Close()`, which in turn calls `Dispose(true)`, which does the full dispose. If you enable Microsoft's code analysis, you will see that it throws a warning on using `Close()` or `Dispose()` in a `using` block, citing double-dispose as the problem. And yes, `StringBuilder.ToString()` method shouldn't throw an exception, but `FileStream.Write()` most definitely could. It's **always** best practice to dispose across all exception paths. – Polynomial Nov 16 '11 at 13:38
  • @polynomial: That's true. Thinking about it I guess I'd be more surprised at Dispose throwing "ObjectDisposedException" but that's some interesting stuff to be aware of. I had assumed that close would just close the stream and not necessarily clear up everything (although I'm not sure what else there is to clear up in this specific case). Certainly I am happy enough that the close is redundant and so is best left out (to avoid code clutter), whether it actually causes problems or not. And I did mean dispose just as in "calls the dispose method". – Chris Nov 16 '11 at 13:46
  • @Polynomial OK, thanks - if code analysis warns about Close then I'm mostly convinced, although I'd rather see it written in MSDN that you shouldn't do it. – Rup Nov 16 '11 at 13:49
  • Yeah, I often find that the documentation only shows part of the story. It's always fun to disassemble the code and see how it really works internally. – Polynomial Nov 16 '11 at 13:50
1

setting UTF8 encoding working with Arabic font is the best thing I did:

 using (var sw = new StreamWriter(

 new FileStream(temporaryFilePath,    
               FileMode.Create,
               FileAccess.ReadWrite), 
               Encoding.UTF8))
            {
                sw.Write(sb.ToString());
            }
 )
1

The easiest way is to use the right constructor.

StreamWriter(String, Boolean, Encoding)

Initializes a new instance of the StreamWriter class for the specified file by using the specified encoding and default buffer size. If the file exists, it can be either overwritten or appended to. If the file does not exist, this constructor creates a new file.

C#

public StreamWriter (string path, bool append, System.Text.Encoding encoding);
Jin Thakur
  • 2,711
  • 18
  • 15