Почему StreamReader.ReadLine() возвращает значение для однострочного файла без новой строки?

Я хочу добавить два текстовых файла вместе.

У меня есть один файл с переводом строки возврата каретки в конце. Обратите внимание на файл A размером 28 байт.

это строка в файле\n

тогда у меня есть другой файл, который является тем же самым без новой строки. Обратите внимание на файл B размером 26 байт.

это строка в файле

Я хочу добавить один и тот же файл к себе (файл A к A и файл B к B) и сравнить количество байтов.

Однако при использовании StreamReader.ReadLine() в файле A я получаю возвращаемое значение, но MSDN говорит:

Строка определяется как последовательность символов, за которой следует перевод строки ("\n"), возврат каретки ("\r") или возврат каретки, за которым сразу следует перевод строки ("\r\n"). Возвращаемая строка не содержит завершающего символа возврата каретки или перевода строки. Возвращаемое значение равно null, если достигнут конец входного потока.

Однако в файле нет crlf.

Как я могу безопасно добавить эти файлы, не добавляя в конце дополнительный разрыв строки? Например, StreamWriter.WriteLine() добавит дополнительный разрыв строки в файл A, когда я этого не хочу. Какой подход был бы идеальным?


person Bryan Crosby    schedule 10.08.2010    source источник
comment
Если вы просто хотите добавить весь файл, то зачем вообще заниматься такими выдуманными конструкциями, как строки? Либо используйте ReadToEnd, чтобы получить все это, либо прочитайте файл по одному блоку за раз. Нет причин специально обращаться с парой символов, когда вы все равно собираетесь записать их дословно.   -  person Rob Kennedy    schedule 10.08.2010
comment
Я был обеспокоен использованием памяти для больших файлов. Но обработка файла кусками имеет смысл.   -  person Bryan Crosby    schedule 10.08.2010


Ответы (4)


StreamReader и StreamWriter (производные от TextReader и TextWriter) не подходят для ситуаций, требующих точной формы двоичных данных. Это высокоуровневые абстракции файла, состоящего из байтов, а не текста или строк. На самом деле, вы можете не только получить разное количество новых строк, но и, в зависимости от среды, вы можете написать признак конца строки, отличный от ожидаемого CR/LF.

Вместо этого вы должны просто скопировать из одного потока в другой. На самом деле это довольно легко.

var bytes = File.ReadAllBytes(pathIn);
var stream = File.Open(pathOut, FileMode.Append);
stream.Write(bytes, 0, bytes.Length);
stream.Close();

Если размер файла потенциально велик, вы должны открыть входной и выходной файл одновременно и использовать буфер фиксированного размера для копирования блоков за раз.

using (var streamIn = File.Open(pathIn, FileMode.Read))
using (var streamOut = File.Open(pathOut, FileMode.Append)) {

    var bytes = new byte[BLOCK_SIZE];

    int count;
    while ((count=streamIn.Read(bytes, 0, bytes.Length)) > 0) {
        streamOut.Write(bytes, 0, count);
    }

}

Также стоит отметить, что приведенный выше код можно заменить на Stream.CopyTo что является новым в .NET 4.

person Josh    schedule 10.08.2010

Вы получите null только в том случае, если вызовете ReadLine в конце потока. В противном случае вы получите все данные до либо CRLF или конца потока.

Если вы пытаетесь выполнить побайтовое дублирование (и сравнение), вам лучше читать либо символы (используя StreamReader/StreamWriter, как вы используете сейчас), либо байты (используя только класс Stream), используя нормальные функции Read и Write, а не ReadLine и WriteLine.

Вы также можете просто прочитать все содержимое файла, используя ReadToEnd, а затем записать его, вызвав Write (не WriteLine), хотя это нецелесообразно, если файл большой.

string data;

using(StreamReader reader = new StreamReader(path))
{
    data = reader.ReadToEnd();
}

using(StreamWriter writer = new StreamWriter(path, true))
{
    writer.Write(data);
}
person Adam Robinson    schedule 10.08.2010

Вы можете использовать StreamWriter.Write вместо WriteLine, чтобы избежать лишнего crlf.

Что касается документов ReadLine, я считаю, что проблема заключается в плохо сформулированном объяснении. Вы, конечно, не хотели бы, чтобы последние байты файла были отброшены только потому, что нет формального флага окончания строки.

person Ray    schedule 10.08.2010

Ну, это действительно зависит от причин вашей реализации (почему вы читаете его построчно и записываете обратно построчно?) Вы можете просто использовать StreamWriter.Write(string) и вывести весь сохраненный текст, WriteLine() названы так, потому что они добавляют новую строку.

Метод TextWriter.WriteLine (String)
Записывает в текстовый поток строку, за которой следует признак конца строки.

person Quintin Robinson    schedule 10.08.2010