Archivo fragmentado y almacenamiento en búfer?

En última instancia, solo estoy buscando cortar un archivo binario en partes que no sean mayores que X en tamaño. No hagas otra cosa con eso. Si el archivo de entrada es de 21 MB, quiero 3 unidades de 7 MB que podría unir con cat o lo que no. En el siguiente ejemplo simple que funciona, estoy usando un búfer de tamaño de fragmento de 7 MB. ¿Tengo que usar eso para obtener fragmentos de archivos de 7MB? Si el tamaño del fragmento era de 2 GB, obviamente no es algo que quiera guardar en la memoria. Así que necesito crear un búfer en absoluto.

Leí varias publicaciones aquí y otros sitios sobre esto, pero todos parecen usar algún tipo de búfer creado por malloc o arrays, y buscar formas sin búfer me lleva demasiado lejos de mi conocimiento en sockets y TCP / IP Temas relacionados.

¿Estoy condenado a un montón de declaraciones if / while?

PD: ¿Dónde puedo encontrar libros sobre flujos de E / S en C? Puedo encontrar mucho para C ++, pero no C.

ifp = fopen(ifile, "rb"); // ifile is a 25MB sound file ofp = fopen(ofile, "w"); // Omitted error checking. setvbuf( ifp, NULL, _IOFBF, 1024); // Are these on setvbuf( ofp, NULL, _IOFBF, 1024); // by default? size_t CHUNK = 7000000; // 7MB Chunk sizes size_t result = 0; size_t *buffer = malloc(CHUNK); if (buffer == NULL) {fputs ("Could not allocate memory",stderr); exit (1);} // Read 1 btye at a time? result = fread(buffer, 1, CHUNK, ifp); if (result != CHUNK) {fputs ("ERROR: Buffer/read mismatch.",stderr); exit (1);} fwrite(buffer, CHUNK, 1, ofp); free(buffer); 

Aquí hay un progtwig, bsplit , que escribí originalmente en 1991. Divide un archivo en trozos de tamaño arbitrario; el tamaño predeterminado se especifica en kilobytes (bueno, kibibytes – 1024 bytes).

 /* @(#)File: $RCSfile: bsplit.c,v $ @(#)Version: $Revision: 1.11 $ @(#)Last changed: $Date: 2008/08/09 05:54:55 $ @(#)Purpose: Split file into blocks -- binary @(#)Author: J Leffler */ #if __STDC_VERSION__ >= 199901L #define _XOPEN_SOURCE 600 #else #define _XOPEN_SOURCE 500 #endif /* __STDC_VERSION__ */ #include  #include  #include  #include  #include  #include "stderr.h" #include "filter.h" #define MAXFILENAMELEN 256 #define KILOBYTE 1024 #define MEGABYTE (KILOBYTE*KILOBYTE) #define GIGABYTE (MEGABYTE*KILOBYTE) #define NIL(x) ((x)0) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) char *prefix = "bsplit."; size_t blocksize = 64; size_t nblocks = 0; size_t skipblocks = 0; char buffer[64*KILOBYTE]; long counter = 0; static int bsplit(FILE *ifp, const char *fn) { size_t n; /* Bytes read this time */ size_t bsize; /* Size written for current block */ size_t tsize; /* Size written for current file */ size_t rsize; /* Amount to read */ FILE *op; /* Output file stream */ char file[MAXFILENAMELEN]; /* Output file name */ tsize = 0; bsize = 0; op = NIL(FILE *); rsize = MIN(sizeof(buffer), blocksize); while ((n = fread(buffer, sizeof(char), rsize, ifp)) > 0) { tsize += n; if (tsize > skipblocks) { if (bsize == 0) { sprintf(file, "%s%03ld", prefix, counter++); if ((op = fopen(file, "w")) == NIL(FILE *)) { err_sysrem2("failed to open file", file); return(-1); } printf("%s\n", file); } bsize += n; if (fwrite(buffer, sizeof(char), n, op) != n) { err_sysrem2("failed to write to file", file); return(-1); } if (bsize >= blocksize) { fclose(op); bsize = 0; } if (nblocks > 0 && tsize >= nblocks) break; } } return 0; } int main(int argc, char **argv) { int opt; size_t multiplier = KILOBYTE; char *p; char c; int rc; opterr = 0; err_setarg0(argv[0]); while ((opt = getopt(argc, argv, "s:n:p:b:V")) != -1) { switch (opt) { case 'p': prefix = optarg; if (strlen(prefix) > MAXFILENAMELEN - sizeof("000")) err_error("file name prefix (%s) is too long (max %d)", prefix, (int)(MAXFILENAMELEN-sizeof("000"))); break; case 's': skipblocks = atoi(optarg); break; case 'n': nblocks = atoi(optarg); break; case 'b': blocksize = atoi(optarg); p = optarg + strspn(optarg, "0123456789"); if (*p != '\0') { c = tolower((unsigned char)*p); if (c == 'c') multiplier = 1; else if (c == 'b') multiplier = KILOBYTE/2; else if (c == 'k') multiplier = KILOBYTE; else if (c == 'm') multiplier = MEGABYTE; else if (c == 'g') multiplier = GIGABYTE; else err_error("unknown size multiplier suffix %s\n", p); if (p[1] != '\0') err_error("unknown size multiplier suffix %s\n", p); } break; case 'V': err_version("BSPLIT", &"@(#)$Revision: 1.11 $ ($Date: 2008/08/09 05:54:55 $)"[4]); break; default: err_usage("[-b blocksize][-p prefix][-s skipblocks][-n blocks][file [...]]"); break; } } /* Convert sizes to bytes */ blocksize *= multiplier; skipblocks *= blocksize; if (nblocks > 0) nblocks = skipblocks + nblocks * blocksize; rc = filter_stdout(argc, argv, optind, bsplit); return(rc); } 

El encabezado stderr.h declara una serie de rutinas de informe de errores; Lo uso en la mayoría de mis progtwigs. El encabezado filter.h declara la función filter_stdout() que recorre una lista de argumentos, abriendo los archivos para leer y llamando a una función, en este caso bsplit() , para procesar cada archivo a su vez. Maneja ‘sin argumentos significa leer entrada estándar’, etc. automáticamente. (Contáctame para el código – mira mi perfil).

Tenga en cuenta que el multiplicador c significa ‘caracteres’, b significa bloques de 512 bytes y k , m g significan KiB, MiB y GiB respectivamente.

 setvbuf( ifp, NULL, _IOFBF, I_BUFFER); // Are these on setvbuf( ofp, NULL, _IOFBF, O_BUFFER); // by default? 

Éstos configuran el búfer de archivo como “totalmente búfer”, lo que significa que los datos solo se escriben cuando el búfer (definido por I_BUFFER y O_BUFFER) está lleno.

También sugeriría que no necesite leer una gran cantidad a la vez. 10-100KB sería más que suficiente para reducir cualquier sobrecarga en el sistema operativo a casi nada, y el bucle para hacer esto varias veces será una proporción tan pequeña que no importará. Si lees porciones más pequeñas y luego escribes la porción pequeña, incluso puedes superponer un poco, donde si lees 7MB de una sola vez, tomará el tiempo suficiente para que una escritura previa de 7MB haya terminado por completo en el momento en que 7MB ha sido leído.

Toda la biblioteca estándar de C está cubierta en http://www.cplusplus.com (a pesar del nombre, cubre las funciones de C así como las funciones de C ++).

No necesita preocuparse por el almacenamiento en búfer en absoluto. Los desarrolladores de la biblioteca de Standard C ya lo han hecho por ti. No perderá mucho rendimiento incluso si lee carácter por carácter con fgetc() y escribe con fputc() . Estas usualmente son macros para acceder a las estructuras stdio en búfer

Confío en que no tengo que decirle cómo escribir un bucle que lee y escribe por caracteres y cambia a un nuevo archivo después del número de bytes deseado.

Para hacerlo de nuevo explícito: la primera regla de optimización: no . El segundo: mida antes que tú.