Discussion:
Ficheros grandes en PHP
(demasiado antiguo para responder)
Sergio Perea
2005-09-05 13:34:09 UTC
Permalink
Hola,

Estoy usando PHP para realziar una generacion de ficheros. Hay un script en
cuestión que genera un fichero de log muy grande, en torno a los 6 gb. El
problema es que cuando el fichero pasa de 4 gb y pico, se corrompe su
contenido. No se si es un problema de configuracion de php o que.,

¿Alguien tiene alguna diea?
Iván Sánchez Ortega
2005-09-05 23:29:08 UTC
Permalink
Post by Sergio Perea
Estoy usando PHP para realziar una generacion de ficheros. Hay un script
en cuestión que genera un fichero de log muy grande, en torno a los 6 gb.
El problema es que cuando el fichero pasa de 4 gb y pico, se corrompe su
contenido. No se si es un problema de configuracion de php o que.,
¿Qué sistema de ficheros estás usando? ¿Qué método usas para hacer crecer el
fichero hasta ese tamaño?

- --
- ----------------------------------
Iván Sánchez Ortega -ivansanchez-arroba-escomposlinux-punto-org

"In the fight between you and the world, back the world."
--Frank Zappa
Sergio Perea
2005-09-06 10:04:32 UTC
Permalink
Pues simplemente abro los ficheros con fopen:

$DescriptoFicheroSalida = fopen("salida.TXT","w+"); # Fichero lineas
aceptadas

y voy escribiendo lineas de esta manera:
# buffer contiene una linea
fwrite($DescriptoFicheroSalida, $buffer, 4096);

El sistema de ficheros es NTFS, y me permite almacenar archivos de 9Gb
tranquilamente

Ejecuto el programa, y veo como el fichero crece rapidamente.
Cuando llega a 4Gb y pico el fichero deja de crecer.
Cuando finaliza el programa, abro el fichero con todo tipo de editores, y es
como si solo tuviera basura. Generando ficheros de menos tamaño, no me da
problemas-.

Muchas gracias anticipadas por tus consejos.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Post by Sergio Perea
Estoy usando PHP para realziar una generacion de ficheros. Hay un script
en cuestión que genera un fichero de log muy grande, en torno a los 6 gb.
El problema es que cuando el fichero pasa de 4 gb y pico, se corrompe su
contenido. No se si es un problema de configuracion de php o que.,
¿Qué sistema de ficheros estás usando? ¿Qué método usas para hacer crecer el
fichero hasta ese tamaño?
- --
- ----------------------------------
Iván Sánchez Ortega -ivansanchez-arroba-escomposlinux-punto-org
"In the fight between you and the world, back the world."
--Frank Zappa
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
iD8DBQFDHNTFR56dWuhgxGgRAo+LAKCgq5BUO3jwu4sx0hmlRSbI8bgv2ACdFT78
kYQ2jo5SLSnw18HXBWCFgG0=
=AhlA
-----END PGP SIGNATURE-----
Manuel Gomez
2005-09-06 14:06:57 UTC
Permalink
Post by Sergio Perea
# buffer contiene una linea
fwrite($DescriptoFicheroSalida, $buffer, 4096);
Has probado con type fichero

mejor con tail -n 100 fichero y ves si se ha grabado el final
Jose Miguel Pérez
2005-09-07 09:22:32 UTC
Permalink
Post by Manuel Gomez
Post by Sergio Perea
# buffer contiene una linea
fwrite($DescriptoFicheroSalida, $buffer, 4096);
Has probado con type fichero
mejor con tail -n 100 fichero y ves si se ha grabado el final
En un mensaje anterior, Sergio dijo que usaba un sistema de ficheros
NTFS por lo que seguramente usará Windows y tail no le funcionará.

Además, no creo que sea que el fichero esté corrupto, sino que el editor
que usa Sergio para leer el archivo no admite ficheros de más de 2Gb.

Sergio, has puesto código incompleto, sólo dices cómo abres el fichero y
como escribes (fopen, fwrite), pero no pones el código intermedio. Esto es
MUY importante. Verás: si abres el fichero con modo "w+", me juego el cuello
a que luego haces un fseek para poner el puntero de escritura al final del
archivo, ¿me equivoco? Usar la función fseek es muy peligroso para este tipo
de archivos mayores de 2Gigas. Asegurate de usar lo siguiente, para
posicionar el puntero:

fseek( $fp, 0, SEEK_END );

De otra forma, estarás usando funciones no válidas para ficheros
enormes. Por ejemplo, esto SI que corromperá el fichero:

fseek( $fp, $tamano_archivo, SEEK_SET );

Tanto Windows como Linux (usando sistemas de ficheros NTFS o ext2/3) son
suficientemente capaces de manejar sin problema ficheros de muuuuchos gigas.
El problema viene con las aplicaciones y por el uso de "int". El tipo de
dato "int" a veces nos olvidamos que su valor máximo son 2Gigas (INT_MAX),
subiendo a 4Gigas si es sin signo (UINT_MAX). (2^31 y 2^32
respectivamente en plataformas de 32 bits). Para manejar ficheros de este
tamaño, hay que usar datos de tipo "long long int" (usando C).

Esto se hace patente viendo el manual de PHP en la página de la función
"filesize" en la que se dice textualmente:

"Nota: Dado que el tipo entero de PHP tiene signo y muchas plataformas usan
enteros de 32 bits, filesize() puede devolver resultados inesperados para
archivos con un tamaño mayor de 2GB. Para archivos entre 2GB y 4GB de
tamaño, esto puede resolverse por lo general usando sprintf("%u",
filesize($archivo))."

Osea, que como me temía PHP usa internamente "int" para representar
valores enteros, en lugar de "long" o "long long". Lo que es peor, en la
página sobre tipos de datos, se especifica claramente:

"El tamaño de un entero es dependiente de la plataforma, aunque un valor
máximo de aproximadamente dos billones es el valor usual (lo que es un valor
de 32 bits con signo). PHP no soporta enteros sin signo."

Resumiendo que me estoy yendo por las ramas, mejor abre el fichero con
modo "a" o "a+", o usa la funcion "error_log" para escribir ficheros de log,
de esta forma debería ser seguro que no se corrompe el archivo.

Como última recomendación, asegurate de que el fichero está corrupto.
Simplemente abriendo el archivo en un editor no lo puedes asegurar porque no
puedes saber si ese editor admite ficheros mayores de 2Gigas. Para estar mas
seguro haz un programa en C u otro lenguaje usando tipos de datos y
funciones correctas para ficheros enormes.

De todas formas, ficheros de log de 2 gigas o más son prácticamente
intratables. Deberías usar alguna forma de partir esos ficheros de log por
ejemplo agrupando la información por días, horas, etc.

Referencias:

- Manual de PHP: Función "filesize":
http://es2.php.net/manual/es/function.filesize.php

- Manual de PHP: Tipos / Enteros:
http://es2.php.net/manual/es/language.types.integer.php

- Manual de PHP: Función "error_log":
http://es2.php.net/manual/es/function.error-log.php

Saludos.
Jose Miguel.
Oscar Garcia
2005-09-07 14:15:17 UTC
Permalink
Post by Sergio Perea
Estoy usando PHP para realziar una generacion de ficheros. Hay un script en
cuestión que genera un fichero de log muy grande, en torno a los 6 gb. El
problema es que cuando el fichero pasa de 4 gb y pico, se corrompe su
contenido. No se si es un problema de configuracion de php o que.,
¿Alguien tiene alguna diea?
Bajo Linux usando PHP 4.3.10 en una Debian 3.1:

#!/usr/bin/php -q
<?php
// Cadena de un megabyte
$mega = str_repeat('01234567890123456789012345678901', 32768);

$f = fopen('salida.txt', 'a');
// Al añadir no hace falta.. pero por si las moscas :)
fseek($f, 0, SEEK_END);
for ($i = 0; $i < 1024; $i++) {
fwrite($f, $mega);
}
fclose($f);
echo filesize('salida.txt') ."\n";
$t = filesize('salida.txt');
echo "$t\n";
?>

***@redstar:~$ ./archivo.php
1073741824
1073741824
***@redstar:~$ ./archivo.php
Superado el límite de tamaño de fichero

Creo que a parte de que PHP tenga problemas o no manejando enteros de
más de 32 bits (sin usar float) las librerías no permiten acceder ni
guardar información en archivos más grandes de 2 gigas.

No sé si han cambiado algo en PHP5, pero en PHP4 está claro que no se
puede usando fopen, fwrite y fclose.

Hace poco en es.comp.lenguajes.c o es.comp.os.linux.programacion
recuerdo que hubo un hilo acerca del manejo de archivos grandes en C y
recuerdo que se llegó a la conclusión de que había que usar librerías
y funciones específicas para eso.

En cuanto busque por los archivos el tema pondré por aquí cómo se
hacía y si hay solución para PHP.

Un saludo a todos.
--
Óscar Javier García Baudet
LinaresDigital
http://redstar.linaresdigital.com/
Jose Miguel Pérez
2005-09-08 09:15:04 UTC
Permalink
Post by Oscar Garcia
Creo que a parte de que PHP tenga problemas o no manejando enteros de
más de 32 bits (sin usar float) las librerías no permiten acceder ni
guardar información en archivos más grandes de 2 gigas.
Por compatibilidad, así es. Pero (hablando de C) deberías usar los tipos
"off_t" "fpos_t", etc, en lugar de long. El problema está en que estas
funciones usan "long" (32 bits) para las operaciones. Por ejemplo, "ftell"
tiene como contrapartida "ftello" que devuelve un "off_t" en lugar de un
"long".

En un programa normal (siempre hablando en máquinas de 32 bits) obtendrás
los siguientes resultados:

sizeof(off_t) = 4
sizeof(size_t) = 4
sizeof(fpost_t) = 12

Como ves, off_t y fpos_t siguen usando enteros de 32 bits, pero si añadimos
los siguientes flags: -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE a la
compilación, nuestro programa obtiene lo siguiente:

sizeof(off_t) = 8
sizeof(size_t) = 4
sizeof(fpost_t) = 16

Como puedes ver, off_t y fpos_t han sido redefinidos para usar "long long"
(entero de 64 bits) y las funciones "ftello", "fseeko", etc, funcionarán.
Además, compilando con estos flags, no aparece el error "Superado el límite
de tamaño de fichero" usando fwrite.

Para comprobarlo hice un programa como el siguiente (parecido, he omitido
cosas por claridad):

// Función para meter un 1Mb en el fichero
// (Simulación de log).
int mete_mega()
{
FILE *fp = fopen("prueba.log", "a+");
fwrite(p, 1, UN_MEGA, fp);
fclose(fp);
return 0;
}

int main()
{
// Meter 1Gb en "prueba.log"
for (int i=0; i<1024; i++) mete_mega();
return 0;
}

Como ves, utilizo las funciones de stream de la librería standard, pero no
uso ftell, fseek, etc. Compilado normalmente:

g++ -o prueba prueba.cpp

...cuando lo ejecuto, al llegar a 2Gb me sale "Superado el límite de tamaño
de fichero". Pero compilado con los flags que te digo:

g++ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -o prueba prueba.cpp

...este es el resultado después de ejecutar "prueba" cinco veces:

$ ls -lh prueba.log
-rw-r--r-- 1 mini yo 5,0G sep 8 11:00 prueba.log
Post by Oscar Garcia
No sé si han cambiado algo en PHP5, pero en PHP4 está claro que no se
puede usando fopen, fwrite y fclose.
El problema no es de la librería standard, como te comento en C sólamente
se mantiene el problema por compatibilidad, supuestamente compilando PHP4
con los flags arriba indicados obtendríamos los mismos resultados. Además,
siempre podemos usar la función "error_log" de PHP. (No se cómo se
comportaría error_log con ficheros mayores de 2Gb).

Piensalo de otra forma ¿qué otro método hay para abrir ficheros? En C sólo
tenemos otra alternativa, las funciones "open", etc, que me temo comparten
los mismos problemas.
Post by Oscar Garcia
Hace poco en es.comp.lenguajes.c o es.comp.os.linux.programacion
recuerdo que hubo un hilo acerca del manejo de archivos grandes en C y
recuerdo que se llegó a la conclusión de que había que usar librerías
y funciones específicas para eso.
Ya te digo, sólo si usas "ftell", "fseek", etc para hacer búsquedas dentro
del fichero (se debería usar ftello, fseeko, etc). Además de compilar con
los flags activando el tema.

Saludos.
Jose Miguel.
Oscar Garcia
2005-09-08 11:49:17 UTC
Permalink
El Thu, 08 Sep 2005 09:15:04 GMT, Jose Miguel Pérez
Post by Jose Miguel Pérez
Post by Oscar Garcia
Hace poco en es.comp.lenguajes.c o es.comp.os.linux.programacion
recuerdo que hubo un hilo acerca del manejo de archivos grandes en C y
recuerdo que se llegó a la conclusión de que había que usar librerías
y funciones específicas para eso.
En cuanto busque por los archivos el tema pondré por aquí cómo
se hacía y si hay solución para PHP.
Ya te digo, sólo si usas "ftell", "fseek", etc para hacer búsquedas dentro
del fichero (se debería usar ftello, fseeko, etc). Además de compilar con
los flags activando el tema.
Todo esto lo sabía porque tal y como te digo hace poco lo discutimos
en otro grupo.

Gracias, me has ahorrado tener que buscar en los archivos de news.

El problema se mantiene cuando intentamos hacer cualquier operacion
dentro de PHP ya que siguen usando enteros de 32 bits.
Post by Jose Miguel Pérez
Piensalo de otra forma ¿qué otro método hay para abrir ficheros? En
C sólo tenemos otra alternativa, las funciones "open", etc, que me
temo comparten los mismos problemas.
He intentado buscar alguna solución válida para crear archivos grandes
en PHP4 y por ahora sólo documentos que hablan de PHP5. Indagaré más
cuando tenga más tiempo (esta semana lo tendré faltal).

Un saludo.
--
Óscar Javier García Baudet
LinaresDigital
http://redstar.linaresdigital.com/
Jose Miguel Pérez
2005-09-08 12:06:26 UTC
Permalink
Post by Oscar Garcia
El problema se mantiene cuando intentamos hacer cualquier operacion
dentro de PHP ya que siguen usando enteros de 32 bits.
[...]
Post by Oscar Garcia
He intentado buscar alguna solución válida para crear archivos grandes
en PHP4 y por ahora sólo documentos que hablan de PHP5. Indagaré más
cuando tenga más tiempo (esta semana lo tendré faltal).
Yo estaba intentando ver si en el fuente de PHP 4 se podía añadir estos
flags en el compilador (via "configure --<algo>") pero me temo que no tengo
tanta experiencia con el tema de los ./configure. :-) Así por lo menos se
podría escribir y leer secuencialmente ficheros de más de 2Gb. Voy a ver si
consigo algo. :-D

Saludos.
Jose Miguel.

Loading...