2009-04-10 14 views
7

¿Hay alguna forma de saber cuántos bytes de una secuencia ha utilizado StreamReader?Bytes consumidos por StreamReader

Tengo un proyecto en el que tenemos que leer un archivo que tiene un encabezado de texto seguido del inicio de los datos binarios. Mi primer intento de leer este archivo era algo como esto:

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; 
} 

El problema es que cuando me puse a _dataOffsetstream.Position, me da la posición de que el StreamReader ha leído, no el final de la cabecera. Tan pronto como lo pensé, esto tenía sentido, pero aún necesito saber dónde está el final del encabezado y no estoy seguro de si hay una manera de hacerlo y de aprovechar StreamReader.

Respuesta

0

Así que su última línea contiene 'DATA' + una cantidad desconocida de bytes de datos. Puede extraer la posición usando IndexOf() con su última línea de lectura. Luego reajuste la corriente. Posición.

Pero no estoy seguro de si debe usar ReadLine() en absoluto en este caso. Tal vez sería mejor leer byte por byte hasta llegar a la marca 'DATA'.

+0

Bueno, eso es ciertamente mi posición alternativa, solo quería ver si había una manera mejor antes de implementarlo. –

1

Por lo tanto, los datos son utf8 (la codificación predeterminada para StreamReader). Esta es una codificación multibyte, por lo que IndexOf sería desaconsejable. Usted podría:

Encoding.UTF8.GetByteCount(string) 

en sus datos hasta el momento, agregando 1 o 2 bytes para el final de línea faltante.

+0

Esa es exactamente mi preocupación si uso los conteos de bytes para las cadenas. No sabré con certeza cuánto agregar para los terminadores de línea. –

+1

Esto no funcionará, hay algunos bytes, utilizados para almacenar información técnica, que se perderán si intentas contar así. P. ej. - hay tres bytes al principio del archivo, que muestra que este archivo tiene codificación Unicode. –

3

Puede averiguar cuántos bytes ha devuelto el StreamReader en realidad (en lugar de leer de la secuencia) de varias maneras, ninguna de ellas demasiado directa.

  1. Obtén el resultado de textReader.CurrentEncoding.GetByteCount(totalLengthOfAllTextRead) y busca este posición en la corriente.
  2. Utilice hackers de reflexión para recuperar el valor de la variable privada del objeto StreamReader que corresponde a la posición actual del byte dentro del búfer interno (diferente de la secuencia - generalmente detrás, pero no más que, por supuesto, igual) . A juzgar por .NET Reflector, esta variable parece llamarse bytePos.
  3. No se moleste en usar un StreamReader, sino implemente su función de ReadLine personalizada construida en la parte superior de Stream o BinaryReader incluso (BinaryReader se garantiza que nunca se leerá más adelante de lo que solicite). Esta función personalizada debe leer de la secuencia char por char, por lo que tendría que usar el objeto de bajo nivel Decoder (a menos que la codificación sea ASCII/ANSI, en cuyo caso las cosas son un poco más simples debido a la codificación de un solo byte) .

La opción 1 va a ser la menos eficiente que imaginaba (ya que efectivamente está recodificando el texto que acaba de decodificar), y la opción 3 la más difícil de implementar, aunque quizás la más elegante. Probablemente recomendaría no usar el truco de la reflexión fea (opción 2), aunque parece tentador, ya que es la solución más directa y solo toma un par de líneas. (Para ser honesto, la clase StreamReader realmente debería exponer esta variable a través de una propiedad pública, pero desafortunadamente no es así). Por lo tanto, al final, depende de usted, pero el método 1 o 3 debería hacer el trabajo lo suficientemente bien. ..

Espero que ayude.

1

Si necesita contar bytes, me gustaría ir con el BinaryReader.Puede tomar los resultados y publicarlos según sea necesario, pero creo que la idea de su posición actual es más confiable (dado que se lee en formato binario, es inmune a los problemas de conjunto de caracteres).

0

Los saltos de línea son fácilmente identificables sin necesidad de decodificar primero la secuencia (excepto algunas codificaciones raramente usadas para archivos de texto como EBCDIC, UTF-16, UTF-32), así que puedes leer cada línea como bytes y luego decodificar toda la línea:

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; 
} 

en lugar de cerrar el flujo y abrirlo de nuevo, podría, por supuesto, sólo seguir la lectura de los datos.

+0

Este método solo funciona para la codificación ASCII/ANSI. Para otras codificaciones, realmente deberías estar usando un decodificador, como he detallado en mi publicación. Además, usar una lista va a ser muy ineficiente. – Noldorin

+0

Sí, no funciona para algunas codificaciones inusuales, agregaré una no sobre eso. Una lista utiliza una matriz de bytes para el almacenamiento, por lo que no hay nada que sea muy ineficaz al respecto. – Guffa

Cuestiones relacionadas