StringReader 省略尾随换行符

本文关键字:换行符 StringReader | 更新日期: 2023-09-27 18:35:14

我注意到,在StreamReaderStringReader上使用ReadLine()时,如果文件或字符串以换行符结尾,则该字符序列将完全丢失。请考虑以下示例:

static void Main(string[] args)
{
    string data = "First Line'r'nSecond Line'r'n'r'n'r'n";
    List<string> lineData = new List<string>();
    string[] splitData = data.Split(
        new string[] { "'r'n" }, 
        StringSplitOptions.None);
    using (StringReader sr = new StringReader(data))
    {
        string line;
        while ((line = sr.ReadLine()) != null)
            lineData.Add(line);
    }
    Console.WriteLine("Raw Line Count: " + splitData.Length);
    Console.WriteLine("StringReader Line Count: " + lineData.Count);
    Console.WriteLine("Split Data: ");
    foreach (string s in splitData)
        Console.WriteLine(string.IsNullOrEmpty(s) ? "[blank line]" : s);
    Console.WriteLine("StringReader Data: ");
    foreach (string s in lineData)
        Console.WriteLine(string.IsNullOrEmpty(s) ? "[blank line]" : s);
    Console.ReadKey();
}

输出如下:

Raw Line Count: 5
StringReader Line Count: 4
Split Data:
First Line
Second Line
[blank line]
[blank line]
[blank line]
StringReader Data:
First Line
Second Line
[blank line]
[blank line]

为什么StringReader/StreamReader会这样? 我可以想到几种解决方法,但是由于读者的行为意想不到,因此必须重新设计我的代码似乎很愚蠢。某些 .NET 库中是否有某些设置会影响流处理最终换行符的方式?

编辑

这是另一个示例:首先将运行示例时的结果与"First Line'r'nSecond Line"进行比较,然后再与"First Line'r'nSecond Line'r'n"进行比较。 结果完全相同(就示例的 StringReader 部分而言)。 为什么 StringReader 会在第二个示例中返回 null 而不是空字符串? 我知道从ReadLine()返回的字符串不包括换行符,但为什么最后一行会被解释为null而不是""

StringReader 省略尾随换行符

这是预期的行为并记录在案。从 - http://msdn.microsoft.com/en-us/library/system.io.stringreader.readline.aspx。

行定义为后跟换行符 ("'")、回车符 ("''r") 或紧跟换行符 ("''r'") 的回车符序列。返回的字符串不包含终止回车符或换行符。如果已到达字符串的末尾,则返回值为 null。

这意味着返回的最后一个值为 null,它将省略最后一个换行符。 如果需要在读取的数据中显示它,可以通过uisng Environment.NewLine重新申请。

根据 ReadLine 上的文档:

行定义为后跟换行符 ("'")、回车符 ("''r") 或紧跟换行符 ("''r'") 的回车符序列。返回的字符串不包含终止回车符或换行符。如果到达输入流的末尾,则返回值为 null。

您正在使用一种依赖于 Environment.NewLine 来标记输入流并返回结果的方法。由于这些令牌已从结果中排除,因此预期的行为是您所看到的是合理的。

如果您需要这些字符,最好分块读取文件(使用带有缓冲区的标准Read)并自己分解内容。或者,您可以创建自己的Stream实现,以根据需要执行任务。

输出的差异不是因为StringReader的奇怪行为。请注意,您的输入仅包含四行,并且正在读取的正好四行(仅没有文档指定的结束换行符)。它是 Split 方法,它引入了一个额外的行 - 因为如果您想保留空条目,则会在最后一个令牌之后创建一个不存在的条目。

StringReader输出:

"First Line'r'nSecond Line'r'n'r'n'r'n";
 ^1st          ^2nd           ^3rd^4th   (line)

Split输出:

"First Line'r'nSecond Line'r'n'r'n'r'n";
 ^1st          ^2nd           ^3rd^4th^5th (token)

考虑以下输入:

"First line'r'n"

有多少行?一个,这就是输出:

Split Data:
First Line
[blank line]
StringReader Data:
First Line

因此,似乎Split是这里的"问题"(如果有的话)。

道格拉斯在下面的评论中描述了真正的问题,那就是像"ABC'r'nXYZ""ABC'r'nXYZ'r'n"这样的输入是无法区分的。但是,在ReadLine界面的典型用例中,您并不关心这一点。如果你想关心,你需要使用一个级别稍低的界面(例如 Read)。