Mishax
Mishax

Reputation: 4582

What is the use of Environment.NewLine and TextWriter.NewLine properties

I have a weird situation that I don't understand relating to newlines '\n' that I am sending to a file. The newlines do not seem to be treated the according to the NewLine properties of TextWriter and Environment. This code demonstrates:

String baseDir = Environment.GetEnvironmentVariable("USERPROFILE") + '\\';
String fileName = baseDir + "deleteme5.txt";
FileInfo fi = new FileInfo(fileName);
fi.Delete();
FileStream fw = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write);
StreamWriter sw = new StreamWriter(fw);
Console.WriteLine(Environment.NewLine.Length);
Console.WriteLine(sw.NewLine.Length);
sw.Write("1\uf0f1\n2\ue0e1\n3\ud0d1\n");
sw.Flush();
sw.Close();

When I run this the console output is

2
2

When I look at my file in hex mode I get:

00000000h: 31 EF 83 B1 0A 32 EE 83 A1 0A 33 ED 83 91 0A ; 1.2.3탑.

Clearly, the API says two characters and when you look in the file there is only one character. Now when I look to the description of the Write method in TextWriter it indicates that the Write method does not substitute 0A with the NewLine property. Well, if the Write method doesn't take that into account, what is the use of having not one but two NewLine properties? What are these things for?

Upvotes: 0

Views: 3779

Answers (4)

Hans Passant
Hans Passant

Reputation: 942119

Programmers have a very long history of not agreeing how text should be encoded when it is written to a file. ASCII and Unicode helped level the Tower of Babel to some degree. But what characters denote the ending of a line was never agreed upon.

  • Windows uses the carriage return + line feed control codes. "\r\n" in C# code.
  • Unix flavors use just a single line feed control code, '\n' in C# code.
  • Apple historically used just a single carriage return control code, '\r' in C# code.

.NET needed to be compatible with all these incompatible choices. So it added the Environment.NewLine property, it has the default line ending sequence for your operating system. Note how you can run .NET code on Unix and Apple machines with Mono or Silverlight.

The abstract TextWriter class needs to know what sequence to use since it writes text files. So it has a NewLine property, its default is the same as Environment.NewLine. Which you almost always use, but you might want to change it if you need to create a text file that's read by a program on another operating system.

The mistake you made in your program is that you hard-coded the line terminator. You used '\n' in your string. This completely bypasses the .NET properties, you'll only ever see the single line feed control code in the text file. 0x0A is a line feed. Your console output displays "2" since that just displays the string length of the NewLine property. Which is 2 on Windows for "\r\n".

The simplest way to use the .NET property is to use WriteLine() instead of Write():

  sw.WriteLine("1\uf0f1");
  sw.WriteLine("2\ue0e1");
  sw.WriteLine("3\ud0d1"); 

Which makes your code nicely readable as well, it isn't any slower at runtime. If you want to keep the one-liner then you could use composite formatting:

  sw.Write("1\uf0f1{0}2\ue0e1{0}3\ud0d1{0}", Environment.NewLine);

Upvotes: 6

Mishax
Mishax

Reputation: 4582

All newlines escaped as \n in a string are single-character ASCII newlines (0x0A) (not Windows newlines 0D0A) and output to streams in writers as 0x0A unless the programmer takes some explicit step to convert these within the string to the format 0D0A.

The TextWriter.NewLine property is used only by methods like WriteLine, and controls the formatting of the implicit newline that is appended as part of the invocation.

The distinction between Environment.NewLine and TextWriter.NewLine is that Environment.NewLine is readonly, only meant to be queried by programmers. (This is different from Java, for instance, where you can change the "system-wide" newline formatting default with System.setProperty("line.separator", x);

In C# you can modify the format of the implicit newline when writing using TextWriter.NewLine, which is initialized to Environment.NewLine. When using TextReader methods that read lines, there is no TextReader.NewLine property. The implicit newline behavior for readers is to break at any 0x0A, 0x0D, or 0D0A

As pointed out by rene the original problem could be resolved by writing:

sw.Write("1\uf0f1\n2\ue0e1\n3\ud0d1\n".Replace("\n", Environment.NewLine));

Upvotes: 0

rene
rene

Reputation: 42494

If choose to generate the 'linebreaks' your self by sending \n to the streamwriter there is no way the framework is going to interfer with that. If you want the framework to honor the NewLine property use the WriteLine method of the writer and set the NewLine property of the Writer.

Adapt your code like so:

sw.NewLine = Environment.NewLine; // StreamWriter uses \r\n by default

sw.WriteLine("1\uf0f1")
sw.WriteLine("2\ue0e1");
sw.WriteLine("3\ud0d1");

Or have a Custom StreamWriter that overrides the Write method:

public class MyStreamWriter:StreamWriter
{
    public MyStreamWriter(Stream s):base(s)
    {
    }

    public override void Write(string s)
    {
        base.Write(s.Replace("\n",Environment.NewLine));
    }   
}

Or if you only have one line that you want to handle:

sw.Write("1\uf0f1\n2\ue0e1\n3\ud0d1\n".Replace("\n", Environment.NewLine));

Upvotes: 1

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

If you use implicitly, as in calling a WriteLine method or explicitly as in Write(String.Concat("Hello", Environment.NewLine), you get the end of line character(s) defined for your environment. If you don't use it and use say '\n' or even '$', then you are saying no matter what environment I'm in, lines will end like I say. If you want to compare behaviour, write a bit of code and run it under windows and linux (mono)

Upvotes: 0

Related Questions