Что означает возвращаемое значение Filestream.Read? Как читать данные кусками и обрабатывать их?

Я совсем новичок в C#, поэтому, пожалуйста, потерпите меня. Я читаю (используя FileStream) данные (фиксированного размера) в небольшой массив, обрабатываю данные, затем снова читаю и так далее до конца файла.

Я думал об использовании чего-то вроде этого:

            byte[] data = new byte[30];
            int numBytesToRead = (int)fStream.Length;
            int offset = 0;

            //reading
            while (numBytesToRead > 0)
            {
                fStream.Read(data, offset, 30);
                offset += 30;
                numBytesToRead -= 30;

                //do something with the data
            }

Но я проверил документацию и их примеры, и они заявили, что возвращаемое значение вышеуказанного метода чтения:

"Тип: System.Int32 Общее количество байтов, прочитанных в буфер. Это может быть меньше, чем количество запрошенных байтов, если это количество байтов в настоящее время недоступно, или ноль, если конец поток достигнут».

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

Спасибо за ваше время и ответы.


person Ben    schedule 22.02.2011    source источник


Ответы (4)


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

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

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

Вы также должны обработать ситуацию, когда метод Read возвращает ноль, что означает, что больше нет данных для чтения. Обычно этого не происходит до тех пор, пока вы не дойдете до конца файла, но если это произойдет, ваш код попадет в вечный цикл.

byte[] data = new byte[30];
int numBytesToRead = (int)fStream.Length;
int offset = 0;

//reading
while (numBytesToRead > 0) {
  int len = fStream.Read(data, 0, data.Length);
  offset += len;
  numBytesToRead -= len;
  if (len == 0 && numBytesToRead > 0) {
    // error: unexpected end of file
  }
  //do something with the data (len bytes)
}
person Guffa    schedule 22.02.2011
comment
Спасибо за ваш ответ. Итак, вы говорите, что при чтении с диска я также должен проверять, len == data.Length ? Может случиться так, что я не получу все необходимые данные? Как бы я тогда изменил код? В случае, если я не получаю все байты: измените смещение на len и вместо data.length используйте data.Length - len? - person Ben; 22.02.2011
comment
@Ben: Это не очень интересно, если len == data.Length, len содержит количество полученных вами байтов, независимо от размера буфера. - person Guffa; 22.02.2011
comment
Да, но я хочу прочитать желаемое количество байтов в массив (который имеет тот же размер, что и один фрагмент данных), а затем что-то сделать с данными, а затем снова прочитать тот же объем данных в тот же массив. Таким образом, я подумал, что должен сравнить желаемое количество байтов (data.length) с фактическим количеством (len). Но я действительно не знаю, как затем получить только недостающие байты из этого фрагмента и обработать его, а затем продолжить чтение в обычном режиме. - person Ben; 22.02.2011
comment
@Ben: Тогда тебе нужна петля в петле. Для внутреннего цикла вы сохраняете смещение в буфере и вызываете .Read(data, offset, data.Length - offset) до тех пор, пока не заполните буфер. - person Guffa; 22.02.2011
comment
Спасибо... Но, как вы сказали, пока это не обязательно. Используют ли профессиональные продукты и другие разработчики этот способ проверки заполнения буфера? Или это пока просто накладные расходы и никто этим не пользуется? - person Ben; 22.02.2011
comment
@Ben: Я надеюсь, что большинство профессиональных продуктов используют методы, определенные в документации, а не полагаются на то, что они всегда ведут себя одинаково и никогда ничего не пойдет не так. Стабильность приложения зависит от того, как оно выдерживает менее идеальные ситуации. Я видел, как приложения работали нормально в течение многих лет и внезапно ломались, когда какое-то незначительное изменение обнаруживало серьезный недостаток, который существовал все это время. - person Guffa; 22.02.2011

Попробуйте прочитать больше, чем доступно в файле. Это можно сделать в следующих двух случаях:

  • Вы пытаетесь прочитать больше байтов, чем общая длина файла
  • Вы находитесь слишком близко к концу файла, чтобы прочитать запрошенное количество байтов.

Кроме того, у Stream есть потомки для подключений к сети, и в этих случаях не всегда легко узнать, сколько байтов будет доступно и когда.

Способ обработки бинарного файла фрагментами выглядит следующим образом:

byte[] buffer = new byte[BUFFER_SIZE];
int inBuffer;
while ((inBuffer = stream.Read(buffer, 0, buffer.Length)) > 0)
{
    // here you have "inBytes" number of bytes in the buffer
}
person Lasse V. Karlsen    schedule 22.02.2011
comment
Спасибо, я понимаю, что вы сказали, но я просто хочу быть уверенным, что при чтении с диска мне нужно проверять (внутри цикла while) значение inBytes, я имею в виду, может ли случиться, что оно меньше буфера.Длина (если я всегда читаю специальными кусками (chunk_size * numberOfReads = file_size)? - person Ben; 22.02.2011

Байты, недоступные в настоящее время, применяются только к потокам, отличным от FileStream, таким как тот, который находится в HttpWebRequest.

FileStream.Read теоретически может вернуть 1 байт. Вы все еще должны быть в состоянии обрабатывать пакеты такого размера.

Но он никогда не вернет 0, если только нет таких проблем, как потеря соединения SMB, удаление файла, антивирус или конец файла.

Есть лучшие способы чтения файлов. Если вы имеете дело с текстовым файлом, рассмотрите возможность использования вместо него System.IO.StreamReader, так как он обрабатывает различную кодировку текста, разрывы строк и многое другое.

Также имейте в виду, что максимальный размер буфера составляет 2 ГБ, поэтому не делайте new buffer[fileStream.Length]

person servermanfail    schedule 22.02.2011

FileStream происходит от Stream, а Stream является очень общим классом, и описание Read происходит от этого универсального класса. Поток также может быть, например, сетевым потоком, и в нем данные могут быть недоступны в данный момент, поскольку они не были отправлены. Для FileStream вы можете предположить, что вы получите три типа возвращаемых значений:

  1. возвращаемое значение == количество байтов для чтения (последний параметр Read): вы находитесь в середине файла
  2. возвращаемое значение ‹ count && возвращаемое значение > 0: Возможно, вы находитесь в конце файла или остальная часть потока просто недоступна.
  3. возвращаемое значение == 0: Вы уже прочитали весь контент. Больше нечего читать.
person Daniel Hilgarth    schedule 22.02.2011
comment
Вы отвечаете неправильно. В документации MSDN говорится, что реализация может возвращать меньше байтов, чем запрошено, даже если конец потока не достигнут. См.: msdn.microsoft.com/en-us/ library/system.io.filestream.read.aspx Таким образом, вы всегда должны проверять возвращаемое значение операции Read(). - person data; 07.07.2014
comment
@data: Насколько я понимаю, вы только что процитировали общее описание Stream.Read. FileStream.Read имеет следующее описание: Возвращаемое значение: общее количество байтов, считанных в буфер. Это может быть меньше запрошенного количества байтов, если это количество байтов в настоящее время недоступно, или ноль, если достигнут конец потока. Для меня это звучит так же, как мой ответ - person Daniel Hilgarth; 07.07.2014
comment
Я процитировал раздел «Примечания» метода FileStream.Read (v4.5). если это количество байтов в настоящее время недоступно, может быть любая причина, а не только конец потока. Например, если аппаратное обеспечение занято или базовый драйвер вернул меньше запрашиваемого. - person data; 07.07.2014
comment
@data: изменил мой ответ. Вы согласны сейчас? - person Daniel Hilgarth; 07.07.2014
comment
Хилграт, я проверил исходный код .NET, и он использует ReadFile из kernel32.dll, который может возвращать меньше, чем запрошено. Существует много неработающего кода, который полагается на вызовы Read(), возвращающие все, что было запрошено. - person data; 07.07.2014
comment
@data: это не отвечает на мой вопрос. Вы согласны с моим обновленным ответом? - person Daniel Hilgarth; 07.07.2014