Forcing StreamWriter to change Encoding
Asked Answered
F

5

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

Firedog answered 16/11, 2011 at 12:5 Comment(0)
S
55

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.

Stier answered 16/11, 2011 at 12:8 Comment(8)
it works but adds an extra step. StreamWriter has already a constructor which accpets an encondingReverence
@Inu - Yes, I'm using that constructor: StreamWriter(Stream, Encoding)Stier
I don't think the FileStream will be disposed correctly, I think you need two nested using blocks.Orthman
@TimAbell When the StreamWriter wrapper is disposed, it calls Close() on the underlying stream, which implicitly disposes the child stream.Stier
ah, okay, useful. :-)Orthman
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);Fortin
@JinThakur Ah yes, so there is! I wrote this back in 2011 so probably wasn't aware of that call. Will edit it in.Stier
@Stier Thanks.Fortin
B
14

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();
    }
}
Basidiomycete answered 16/11, 2011 at 12:20 Comment(0)
N
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.)

Nonrepresentational answered 16/11, 2011 at 12:8 Comment(9)
Sorry I dont know what is wrapping! would be nice to see a bit of code please!Firedog
"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.Stier
@Polynomial: I can understand dispose calling close but surely close shouldn't dispose of an object should it?Carrousel
@Carrousel - 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: pastebin.com/n9d8TKnsStier
@Stier 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 says a Dispose method must be callable multiple times without throwing an exception, and I've seen no documentation that says Close == Dispose always.Nonrepresentational
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.Stier
@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".Carrousel
@Stier 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.Nonrepresentational
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.Stier
M
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());
            }
 )
Mosa answered 9/4, 2013 at 7:57 Comment(0)
F
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);
Fortin answered 6/1, 2020 at 17:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.