Байты, используемые StreamReader

Есть ли способ узнать, сколько байтов потока было использовано StreamReader?

У меня есть проект, в котором нам нужно прочитать файл с текстовым заголовком, за которым следует запуск двоичных данных. Моя первая попытка прочитать этот файл была примерно такой:

private int _dataOffset;
void ReadHeader(string path) 
{
    using (FileStream stream = File.OpenRead(path)) 
    {
        StreamReader textReader = new StreamReader(stream);

        do 
        {
            string line = textReader.ReadLine();
            handleHeaderLine(line);
        } while(line != "DATA") // Yes, they used "DATA" to mark the end of the header

        _dataOffset = stream.Position;
    }
}

private byte[] ReadDataFrame(string path, int frameNum) 
{
    using (FileStream stream = File.OpenRead(path)) 
    {
        stream.Seek(_dataOffset + frameNum * cbFrame, SeekOrigin.Begin);

        byte[] data = new byte[cbFrame];
        stream.Read(data, 0, cbFrame);

        return data;
    }
    return null;
}

Проблема в том, что когда я устанавливаю _dataOffset в stream.Position, я получаю позицию, которую прочитал StreamReader, а не конец заголовка. Как только я подумал об этом, это имело смысл, но мне все еще нужно было знать, где находится конец заголовка, и я не уверен, есть ли способ сделать это и по-прежнему использовать StreamReader.

7 голосов | спросил Jon Norton 10 PMpFri, 10 Apr 2009 15:56:28 +040056Friday 2009, 15:56:28

5 ответов


0

Вы можете узнать, сколько байтов фактически вернуло StreamReader (в отличие от чтения из потока) несколькими способами. Боюсь, никто из них не слишком прост.

  1. Получите результат textReader.CurrentEncoding.GetByteCount(totalLengthOfAllTextRead) и затем найдите эту позицию в потоке.
  2. Используйте некоторые методы отражения, чтобы получить значение закрытой переменной объекта StreamReader, которая соответствует текущей позиции байта во внутренней буфер (отличается от потока с потоком - обычно позади, но не больше, чем, конечно,). Судя по .NET Reflector, эта переменная называется bytePos.
  3. Не пытайтесь вообще использовать StreamReader, а вместо этого реализуйте свою пользовательскую функцию ReadLine, построенную поверх Stream или BinaryReader даже (BinaryReader гарантированно никогда не будет читать дальше, чем вы запрашиваете). Эта пользовательская функция должна читать из потока char по char, так что вам действительно придется использовать низкоуровневый объект Decoder (если только кодировка ASCII /ANSI, и в этом случае все немного проще из-за однобайтовой кодировки).

Вариант 1 будет наименее эффективным, как я себе представляю (поскольку вы эффективно перекодируете текст, который вы только что декодировали), а вариант 3 сложнее всего реализовать, хотя, возможно, и наиболее элегантен. Вероятно, я бы рекомендовал не использовать уродливое отражение (вариант 2), хотя это выглядит заманчиво, являясь самым прямым решением и занимая всего пару строк. (Если честно, класс StreamReader действительно должен предоставлять эту переменную через открытое свойство, но, увы, нет.) Так что в в конце концов, решать вам, но метод 1 или 3 должен выполнять работу достаточно хорошо ...

Надеюсь, это поможет.

ответил Noldorin 10 PMpFri, 10 Apr 2009 16:13:45 +040013Friday 2009, 16:13:45
0

Итак, данные - utf8 (кодировка по умолчанию для StreamReader). Это многобайтовая кодировка, поэтому IndexOf будет нецелесообразным. Вы могли бы:

Encoding.UTF8.GetByteCount(string)

к вашим данным, добавив 1 или 2 байта для конца отсутствующей строки.

ответил spender 10 PMpFri, 10 Apr 2009 16:10:14 +040010Friday 2009, 16:10:14
0

Если вам нужно подсчитать байты, я бы выбрал BinaryReader. Вы можете взять результаты и привести их в соответствие с необходимостью, но я считаю, что его представление о его текущем положении более надежно (поскольку оно читает в двоичном формате, неуязвимо для проблем с набором символов).

ответил GWLlosa 10 PMpFri, 10 Apr 2009 17:43:13 +040043Friday 2009, 17:43:13
0

Итак, ваша последняя строка содержит «ДАННЫЕ» + неизвестное количество байтов данных. Вы можете извлечь позицию, используя IndexOf () с вашей последней прочитанной строкой. Затем перенастройте поток. Положение.

Но я не уверен, стоит ли вообще использовать ReadLine () в этом случае. Возможно, было бы лучше читать побайтово, пока вы не достигнете отметки «DATA».

ответил tanascius 10 PMpFri, 10 Apr 2009 16:06:24 +040006Friday 2009, 16:06:24
0

Разрывы строк легко идентифицируются без необходимости сначала декодировать поток (за исключением некоторых кодировок, редко используемых для текстовых файлов, таких как EBCDIC, UTF-16, UTF-32), поэтому вы можете просто прочитать каждую строку в байтах, а затем декодировать вся строка:

using (FileStream stream = File.OpenRead(path)) {
   List<byte> buffer = new List<byte>();
   bool hasCr = false;
   bool done = false;
   while (!done) {
      int b = stream.ReadByte();
      if (b == -1) throw new IOException("End of file reached in header.");
      if (b == 13) {
         hasCr = true;
      } else if (b == 10 && hasCr) {
         string line = Encoding.UTF8.GetString(buffer.ToArray(), 0, buffer.Count);
         if (line == "DATA") {
            done = true;
         } else {
            HandleHeaderLine(line);
         }
         buffer.Clear();
         hasCr = false;
      } else {
         if (hasCr) buffer.Add(13);
         hasCr = false;
         buffer.Add((byte)b);
      }
   }
   _dataOffset = stream.Position;
}

Вместо того, чтобы закрывать поток и открывать его снова, вы, конечно, можете просто читать данные.

ответил Guffa 10 PMpFri, 10 Apr 2009 19:35:44 +040035Friday 2009, 19:35:44

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132