none
C# Tengo problemas con leer archivos binarios de gran tamaño como de 4gb RRS feed

  • Pregunta

  • Bueno como dije en el titulo tengo problemas para leer archivos de gran tamaño, es decir no puedo leer ni mas de 2gb ni 4gb espero que me puedan dar una mano con esto y dejo los posibles codigos que uso para leer archivos binarios.

    La primera uso Filestream: aca tengo problema que en el arreglo de bytes llamado datos no puede recibir mas que el limite que tiene un int es decir mas de los 2 mil millones de bytes = 2gb.

        using (Filestream fs = new Filestream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
             byte[] datos = new byte[fs.Length];
             int posicion = 0;
             int cantidadALeer = (int)fs.Length;
             int cantidadLeida = fs.Read(datos, posicion, cantidadALeer);
             BinaryReader reader = new BinaryReader(fs);
        }

    ----------------
    El segundo intento leer con un metodo que eh creado: Bueno el codigo no es muy distinto que el primero pero me sale el mismo error que no puede recibir mas que el limte que tiene un int.

         byte[] buffer = FileToArray(path);

         public byte[] FileToArray(string File)
         {
              byte[] buffer;
              using (Filestream stream = new Filestream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
              {
                  buffer = new byte[stream.Length];
                  int length = (int)stream.Length;
                  int offset = 0;
                  while(length > 0)
                  {
                      int num3 = stream.Read(buffer, offset, length);
                      if (num3 == 0);
                      {
                          break;
                      }
                      offset += num3;
                      length -= num3;
                  }
                  length = buffer.Length;
              }
              return buffer;
         }

    ----------
    El tercer intento uso File:
    pero pasa que este tambien tiene limite de 2gb asi que no lee mas que eso.

         byte[] readFile = File.ReadAllBytes(path);

    ----------
    Y el ultimo que no me funcionaria para archivos binarios ya que los binarios llevan numeros negativos asi que se perderian en la lectura pero lo pongo igual este lee mas de 2gb pero tengo ese problema que se perderian datos poniendo esos numeros negativos como un signo de interrogacion "?".

        using (StreamReader stream = new StreamReader(path))
        {
             string text;
             while (!stream.EndOfStream)
             {
                 text = stream.ReadLine();
             }
        }

    Edit:
    Si es posible dividir el archivo en cada gb y meterlo en varios arreglos de bytes hasta que termine de leer todo el archivo ya seria suficiente, pero despues de un rato me acorde que estaria en otro problema de escritura ya que tampoco habria manera de unir varios arreglos de un 1gb juntos? espero que haya una alternativa sencilla para todo este lio.

    Ya se que es largo pero puse todo lo que tenia para leer archivos binarios espero que alguien haya conseguido poder leer archivos de mas de 2gb y 4gb tambien en C# y lo comparta ya que leer esa cantidad de datos en la actualidad es muy necesario

    Desde ya muchisimas gracias.

    viernes, 1 de enero de 2021 7:46

Respuestas

  • Una primera cosa que tienes que tener en cuenta es que en un array no puede haber más de dos mil millones de elementos, porque se indexa con un integer. Por tanto, no puedes leer to fichero a un único byte[], porque no le caben tantos elementos. No tendrás más remedio que leer el archivo "a pedacitos". Cada pedacito puede medir hasta 2 GB.

    El procedimiento bueno para hacer esto es el segundo que pusiste, es decir, construir un FileStream y llamar a su método Read pasándole la longitud a leer. Pero cerciorándote de que esa longitud es menor de 2GB. Puedes repetirlo en un bucle volviendo a leer de 2 en 2 GB. Podrías guardarlo en un array de arrays, o en un array bidimensional, guardando cada buffer en una fila. Si optas por esta solución, recuerda que necesitas un entorno de 64 bits y que en el app.config tienes que habilitar gcAllowVeryLargeObjects para que soporte objetos mayores de 2GB. Nota: este parámetro requiere al menos el Framework 4.5, y solo aumenta el tamaño total de los arrays, pero no el número de elementos, que sigue limitado a los que se pueden indexar con un int.

    viernes, 1 de enero de 2021 10:37
    Moderador
  • Tal y como te ha comentado Alberto, la forma correcta de leer fichero muy grandes es a pedacitos o chunks. 

    private void ReadFile(string filePath)
    {
        const int MAX_BUFFER = 20*1024*1024; //20MB this is the chunk size read from file
        byte[] buffer = new byte[MAX_BUFFER];
        int bytesRead;
    
        using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read))
        using (BufferedStream bs = new BufferedStream(fs))
           {
              while ((bytesRead = bs.Read(buffer, 0, MAX_BUFFER)) != 0) //reading only 20mb chunks at a time
                  {
                       //buffer contains the chunk data Treasure the moments with it . . . 
                       //modify the buffer size above to change the size of chunk . . .
                  }
           }
    }


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    viernes, 1 de enero de 2021 15:07
    Moderador

Todas las respuestas

  • Una primera cosa que tienes que tener en cuenta es que en un array no puede haber más de dos mil millones de elementos, porque se indexa con un integer. Por tanto, no puedes leer to fichero a un único byte[], porque no le caben tantos elementos. No tendrás más remedio que leer el archivo "a pedacitos". Cada pedacito puede medir hasta 2 GB.

    El procedimiento bueno para hacer esto es el segundo que pusiste, es decir, construir un FileStream y llamar a su método Read pasándole la longitud a leer. Pero cerciorándote de que esa longitud es menor de 2GB. Puedes repetirlo en un bucle volviendo a leer de 2 en 2 GB. Podrías guardarlo en un array de arrays, o en un array bidimensional, guardando cada buffer en una fila. Si optas por esta solución, recuerda que necesitas un entorno de 64 bits y que en el app.config tienes que habilitar gcAllowVeryLargeObjects para que soporte objetos mayores de 2GB. Nota: este parámetro requiere al menos el Framework 4.5, y solo aumenta el tamaño total de los arrays, pero no el número de elementos, que sigue limitado a los que se pueden indexar con un int.

    viernes, 1 de enero de 2021 10:37
    Moderador
  • Tal y como te ha comentado Alberto, la forma correcta de leer fichero muy grandes es a pedacitos o chunks. 

    private void ReadFile(string filePath)
    {
        const int MAX_BUFFER = 20*1024*1024; //20MB this is the chunk size read from file
        byte[] buffer = new byte[MAX_BUFFER];
        int bytesRead;
    
        using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read))
        using (BufferedStream bs = new BufferedStream(fs))
           {
              while ((bytesRead = bs.Read(buffer, 0, MAX_BUFFER)) != 0) //reading only 20mb chunks at a time
                  {
                       //buffer contains the chunk data Treasure the moments with it . . . 
                       //modify the buffer size above to change the size of chunk . . .
                  }
           }
    }


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    viernes, 1 de enero de 2021 15:07
    Moderador
  • Hola perdon por responder tarde muchisimas gracias a los dos parece que funciono en copiar todo los datos, ya que revise copiando cada arreglo con WriteAllBytes para ver si copiaba y no se saltaba ningun dato, efectivamente funciono y estoy contento con el resultado, aunque tengo problemas con los arreglos bidimensionales, la verdad es la primera vez que lo uso y no se extraer datos de ahi, ya que en muchas ocaciones uso, WriteAllbytes, Array.copy y listas de bytes para copiar fragmentos de un lado al otro, lo que me sale son errores como lo siguiente.

            private byte[,] buffer2;
            private int[] chunksoffset;

            private void LeerAFSgrandes(string path)
            {
                using (FileStream isoStream = File.Open(path, FileMode.Open, FileAccess.ReadWrite))
                {
                    CDReader cd = new CDReader(isoStream, true);
                    using (Stream fileStream = cd.OpenFile(@"DATA\ZS2EU_2.AFS", FileMode.Open))
                    {
                        const int MAX_BUFFER = 1000 * 1000 * 1000; //20MB this is the chunk size read from file
                        int bytesRead; int i = 0;
                        byte[] buffer = new byte[MAX_BUFFER];
                        long chunkoffset = fileStream.Length;
                        int cont = 1;
                        chunksoffset = new int[10];

                        while (chunkoffset >= MAX_BUFFER)
                        {
                            cont += cont;
                            chunkoffset -= MAX_BUFFER;
                            chunksoffset[i] = MAX_BUFFER;
                            i++;
                        }
                        chunksoffset[i] = (int)chunkoffset;

                        using (BufferedStream bs = new BufferedStream(fileStream))
                        {
                            i = 0;
                            while ((bytesRead = bs.Read(buffer, 0, chunksoffset[i])) != 0) //reading only 20mb chunks at a time
                            {
                                //buffer contains the chunk data Treasure the moments with it . . .
                                //modify the buffer size above to change the size of chunk . . .
                                buffer2 = new byte[i, chunksoffset[i]];
                                int value = chunksoffset[i];
                                byte[] bufferaux = new byte[value];
                                for(int j = 0; j <= value; j++)
                                {
                                    bufferaux[j] = buffer2[i, j]; //Aqui trato de copiar con un for la cantidad que tiene buffer2[0, value] a bufferaux y aunque trato de copiar pocos datos me sale fuera de memoria
                                }
                                Array.Copy(buffer2[i, value], buffer, value); //la segunda uso Array.copy y me sale de error que no se puede convertir de 'byte' a 'System.Array'
                                
                                File.WriteAllBytes(@"C://Users//dendrobyte//Desktop//pzs3eu1.AFS" + i, buffer2[i, chunksoffset[i]]); //la tercera uso WriteAllBytes y me sale el error de que no se puede convertir 'byte' a 'byte[]'
                                i++;
                            }
                        }
                    }
                }
            }

    Yo pensaba que se podria tomar una porcion de ese arreglo bidimensional poniendo [1, 10000] algo asi y me lo tomaba como un arreglo de bytes comun.

    Desde ya muchisimas gracias por la ayuda ya que exceptuando este problema funciona de maravilla


    sábado, 2 de enero de 2021 17:42