StringReader 省略尾随换行符
本文关键字:换行符 StringReader | 更新日期: 2023-09-27 18:35:14
我注意到,在StreamReader
或StringReader
上使用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
而不是""
?
这是预期的行为并记录在案。从 - 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)。