我必须处理一个文本文件并检查它是否以回车符结束。
我必须阅读整个内容,进行一些更改并将其重新写入目标文件,保持与原始格式完全相同的格式。这就是问题所在:我不知道原始文件最后是否包含换行符。
我已经尝试过了:
如何有效地阅读文件的所有文本并确定它是否以换行符结束?
答案 0 :(得分:6)
通过ReadLine()
读取文件后,您可以在文件结尾前回溯两个字符,并将这些字符与CR-LF进行比较:
string s;
using (StreamReader sr = new StreamReader(@"C:\Users\User1\Desktop\a.txt", encoding: System.Text.Encoding.UTF8))
{
while (!sr.EndOfStream)
{
s = sr.ReadLine();
//process the line we read...
}
//if (sr.BaseStream.Length >= 2) { //ensure file is not so small
//back 2 bytes from end of file
sr.BaseStream.Seek(-2, SeekOrigin.End);
int s1 = sr.Read(); //read the char before last
int s2 = sr.Read(); //read the last char
if (s2 == 10) //file is end with CR-LF or LF ... (CR=13, LF=10)
{
if (s1 == 13) { } //file is end with CR-LF (Windows EOL format)
else { } //file is end with just LF, (UNIX/OSX format)
}
}
答案 1 :(得分:2)
因此,您正在处理文本文件,这意味着您需要阅读所有文本,并希望保留任何换行符,即使在文件末尾也是如此。
您已正确地断定ReadLine()
吃掉了那些,即使文件没有以一个结尾。事实上,当文件以一个文件结尾时,ReadLine()
会吃掉最后一个回车符(StreamReader.EndOfStream
在读取倒数第二行后为true
。 ReadAllText()
also eats the last newline。鉴于您可能正在处理大型文件,您也不希望立即读取内存中的整个文件。
您也不能只比较文件的最后两个字节,因为有些编码使用多个字节来编码字符,例如UTF-16。因此,您需要读取可识别编码的文件。 StreamReader就是这么做的。
因此,解决方案是创建自己的ReadLine()
版本,其中包含最后的换行符:
public static class StreamReaderExtensions
{
public static string ReadLineWithNewLine(this StreamReader reader)
{
var builder = new StringBuilder();
while (!reader.EndOfStream)
{
int c = reader.Read();
builder.Append((char) c);
if (c == 10)
{
break;
}
}
return builder.ToString();
}
}
然后,您可以检查上一个返回的行是否以\n
结尾:
string line = "";
using (var stream = new StreamReader(@"D:\Temp\NewlineAtEnd.txt"))
{
while (!stream.EndOfStream)
{
line = stream.ReadLineWithNewLine();
Console.Write(line);
}
}
Console.WriteLine();
if (line.EndsWith("\n"))
{
Console.WriteLine("Newline at end of file");
}
else
{
Console.WriteLine("No newline at end of file");
}
尽管StreamReader
经过了大量优化,但我无法保证每次阅读一个字符的性能。与ReadLine()
相比,使用两个相同的100 MB文本文件进行快速测试显示出相当大的减速(~1800 vs~400 ms)。
此方法确实保留了原始行结尾,这意味着您可以使用此扩展方法返回的字符串安全地重写文件,而不会将所有\n
更改为\r\n
,反之亦然。