0

我有一个奇怪的情况,我不理解与'\n'我发送到文件的换行符有关的情况。换行符似乎没有根据 和 的NewLine属性TextWriter进行处理Environment。此代码演示:

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();

当我运行这个控制台输出是

2
2

当我以十六进制模式查看文件时,我得到:

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

显然,API 说了两个字符,当您查看文件时,只有一个字符。现在,当我查看 TextWriter 中 Write 方法的描述时,它表明 Write 方法不会用 NewLine 属性替换 0A。好吧,如果 Write 方法没有考虑到这一点,那么没有一个而是两个 NewLine 属性有什么用呢?这些东西是干什么用的?

4

4 回答 4

6

程序员在将文本写入文件时不同意如何编码的历史悠久。ASCII 和 Unicode 在一定程度上帮助平衡了巴别塔。但是从来没有就什么字符表示一行的结尾达成一致。

  • Windows 使用回车 + 换行控制代码。 "\r\n"在 C# 代码中。
  • '\n'Unix 风格在 C# 代码中只使用一个换行控制代码。
  • '\r'Apple 过去在 C# 代码中只使用了一个回车控制代码。

.NET 需要与所有这些不兼容的选择兼容。所以它添加了 Environment.NewLine 属性,它具有操作系统的默认行结束顺序。请注意如何使用 Mono 或 Silverlight 在 Unix 和 Apple 机器上运行 .NET 代码。

抽象 TextWriter 类需要知道要使用什么序列,因为它写入文本文件。所以它有一个NewLine 属性,它的默认值和Environment.NewLine 一样。您几乎总是使用它,但如果您需要创建一个由另一个操作系统上的程序读取的文本文件,您可能需要更改它。

您在程序中犯的错误是您硬编码了行终止符。您在字符串中使用了 '\n' 。这完全绕过了 .NET 属性,您只会在文本文件中看到单换行控制代码。0x0A 是换行符。您的控制台输出显示“2”,因为它只显示 NewLine 属性的字符串长度。在 Windows 上,“\r\n”是 2。

使用 .NET 属性的最简单方法是使用 WriteLine() 而不是 Write():

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

这也使您的代码具有很好的可读性,在运行时也不会变慢。如果要保留单行,则可以使用复合格式:

  sw.Write("1\uf0f1{0}2\ue0e1{0}3\ud0d1{0}", Environment.NewLine);
于 2013-08-04T13:46:39.047 回答
1

如果选择通过将 \n 发送到 streamwriter 来生成你自己的“换行符”,那么框架就不会干扰它。如果您希望框架支持 NewLine 属性,请使用 writer 的 WriteLine 方法并设置 Writer 的 NewLine 属性。

像这样调整您的代码:

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

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

或者有一个覆盖 Write 方法的自定义 StreamWriter:

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

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

或者,如果您只有一行要处理:

sw.Write("1\uf0f1\n2\ue0e1\n3\ud0d1\n".Replace("\n", Environment.NewLine));
于 2013-08-04T09:59:28.840 回答
0

在字符串中转义为 \n 的所有换行符都是单字符 ASCII 换行符 (0x0A)(不是 Windows 换行符 0D0A),并作为 0x0A 输出到写入器中的流,除非程序员采取一些明确的步骤将字符串中的这些换行符转换为格式 0D0A。

TextWriter.NewLine 属性仅由 WriteLine 等方法使用,并控制作为调用一部分附加的隐式换行符的格式。

Environment.NewLine和之间的区别在于TextWriter.NewLineEnvironment.NewLine 是只读的,仅供程序员查询。(这与 Java 不同,例如,您可以在其中更改“系统范围”的换行格式默认值System.setProperty("line.separator", x);

在 C# 中,您可以在使用 编写时修改隐式换行符的格式,该格式TextWriter.NewLine初始化为Environment.NewLine. 使用读取行的 TextReader 方法时,没有TextReader.NewLine属性。阅读器的隐式换行行为是在任何 0x0A、0x0D 或 0D0A 处中断

正如 rene 所指出的,原始问题可以通过以下方式解决:

sw.Write("1\uf0f1\n2\ue0e1\n3\ud0d1\n".Replace("\n", Environment.NewLine));
于 2013-08-05T07:47:45.090 回答
0

如果您隐式使用,如调用 WriteLine 方法或显式使用 Write(String.Concat("Hello", Environment.NewLine),您将获得为您的环境定义的行尾字符。如果您不使用它并使用say'\n'甚至'$',那么你是说无论我在什么环境中,行都会像我说的那样结束。如果你想比较行为,写一些代码并运行它windows和linux下(单声道)

于 2013-08-04T10:13:23.223 回答