2011-03-03 16 views
9

Necesito leer una línea de texto (terminada por una nueva línea) sin hacer suposiciones sobre la longitud. Así que ahora se enfrentan a posibilidades:C fgets versus fgetc para leer la línea

  • Uso fgets y comprobar cada vez si el último carácter es un salto de línea y añadir de forma continua a un búfer
  • Lea cada carácter utilizando fgetc y ocasionalmente realloc el buffer

La intuición me dice que la variante fgetc puede ser más lenta, pero de nuevo no veo cómo fgets puede hacerlo sin examinar todos los caracteres (también mi intuición no siempre es tan buena). Las líneas son bastante grandes, por lo que el rendimiento es importante.

Me gustaría saber los pros y los contras de cada enfoque. Gracias de antemano.

Respuesta

1

Sugiero usar fgets() junto con la asignación de memoria dinámica - o puede investigar la interfaz a getline() que está en el estándar POSIX 2008 y está disponible en máquinas Linux más recientes. Eso hace las cosas de asignación de memoria para ti. Debe mantener las pestañas en la longitud del búfer, así como su dirección, por lo que incluso puede crear una estructura para manejar la información.

Aunque fgetc() también funciona, es ligeramente más extraño, pero solo marginalmente. Debajo de las cubiertas, utiliza los mismos mecanismos que fgets(). Las partes internas pueden explotar una operación más rápida, análoga a strchr(), que no están disponibles cuando llama al fgetc() directamente.

+0

La única limitación al implementar una función 'getline' con' fgets' es que es imposible manejar bytes nulos ** y ** archivos que no terminan con un carácter de nueva línea al mismo tiempo. Si 'fgets' encuentra una condición EOF y regresa sin un carácter de nueva línea, solo puede suponer que la cadena termina en el primer byte nulo. (En otros casos, puede hacer 'strchr (buf, '\ n')' para averiguar dónde se detuvo la lectura, o si no hay ''\ n'', debe' realloc'.) – mk12

+0

Si el el archivo contiene bytes nulos, no es un archivo de texto. (Puede ser un archivo de caracteres anchos, pero luego necesita usar funciones de E/S de caracteres anchos para leerlo). Y 'fgets()' no está diseñado para manejar archivos que contienen bytes nulos, precisamente porque no da un indicación confiable de cuántos bytes leyó. Si su archivo de datos contiene bytes nulos, debería (probablemente) no usar 'fgets()' para leerlo. –

+0

http://linux.die.net/man/3/getline (sección Valor de retorno) parece sugerir que podría ser una cosa útil. De ahí saqué la idea, aunque supongo que estoy de acuerdo contigo. Ahora que lo pienso, tal vez solo se mencione allí porque podría ser útil al usar un delimitador que no sea ''\ n''. – mk12

0

Si puede establecer una longitud de línea máxima, incluso una grande, entonces uno fgets haría el truco. De lo contrario, varias llamadas fgets seguirán siendo más rápidas que las llamadas múltiples fgetc porque la sobrecarga de esta última será mayor.

Una mejor respuesta, sin embargo, es que no vale la pena preocuparse por la diferencia de rendimiento hasta que tenga que hacerlo y a menos que sea necesario. Si fgetc es lo suficientemente rápido, ¿qué importa?

+0

También tenga en cuenta que 'getc' generalmente se implementa como una macro y, por lo tanto, es más rápido que' fgetc', y debe usarse siempre que tenga cuidado (el argumento no puede ser una expresión). – mk12

2

¿Su entorno proporciona la función getline(3)? Si es así, yo diría que síganlo.

La gran ventaja que veo es que asigna el búfer en sí (si lo desea) y realloc() el búfer que pasa si es demasiado pequeño. (Entonces, esto significa que debe pasar algo obtenido del malloc()).

Esto elimina el dolor de fgets/fgetc, y puede esperar que quien escribió la biblioteca C que lo implementa se encargue de hacerlo eficiente.

Bonificación: la página man de Linux tiene un buen ejemplo de cómo usarla de manera eficiente.

+0

Lamentablemente (lo siento, no mencioné esto en la pregunta) Necesito usar cosas estándar :-(Las funciones getline realmente suenan atractivas. – nc3b

+1

Bueno, es estándar (para alguna definición de estándar). Ver [The Open Group Especificaciones base, número 7] (http://pubs.opengroup.org/onlinepubs/9699919799/), también conocido como "IEEE Std 1003.1 ™ -2008", también conocido como "POSIX C 2008". Pero estándar! = Generalizada, desafortunadamente. Siento tu dolor . getline es sexy :-) – Mat

+0

'la funcionalidad getline()' es buena; el nombre 'getline()' es una intrusión atroz en el espacio de nombres de usuario, anticipando uno de los nombres de función más utilizados (por ejemplo, vea K & R 1 y 2) con una amplia gama de diversas interfaces. Fue una * terrible decisión tomar ese nombre; fue una decisión * excelente * para proporcionar la funcionalidad. Lo único sorprendente es la omisión de la capacidad de manejar terminaciones de línea CRLF; la función 'getdelim()' relacionada puede manejar terminaciones de línea CR o LF o NUL, pero no puede manejar terminaciones de línea CRLF. –

0

Asignaría un gran buffer y luego usaría fgets, checking, reallocing y repetiría si no ha leído hasta el final de la línea.

Cada vez que lee (ya sea a través de fgetc o fgets) está realizando una llamada al sistema que lleva tiempo, desea minimizar el número de veces que pasa, por lo que las llamadas se vuelven menos frecuentes y la iteración en la memoria es más rápida.

Si está leyendo un archivo, mmap() ing en el archivo es otra opción.

+0

Tengo que contradecirlo en la parte llamada del sistema: la biblioteca stdio hace el almacenamiento en búfer así que no creo que cada llamada a la función se traduzca en una llamada al sistema. Puedo estar equivocado – nc3b

+0

esto es cierto, pero con Fgets él tendrá un control más fino. si tiene alguna idea de cuánto tiempo duran las líneas en promedio, puede optimizar las longitudes de la memoria intermedia, en lugar de fgetc, que almacenará en búfer pero será completamente independiente de las longitudes ideales de la memoria intermedia. –

2

Si el rendimiento es importante para usted, generalmente desea llamar al getc en lugar de fgetc. El estándar intenta facilitar la implementación de getc como una macro para evitar la sobrecarga de llamadas a la función.

Pasado eso, probablemente la estrategia principal para asignar el búfer es lo principal. La mayoría de las personas usa incrementos fijos (por ejemplo, cuando/si nos quedamos sin espacio, asignamos otros 128 bytes). Aconsejaría utilizar un factor constante , por lo que si se queda sin espacio, asigne un buffer que sea, digamos, 1 1/2 veces el tamaño anterior.

Especialmente cuando getc se implementa como una macro, la diferencia entre getc y fgets suele ser bastante mínima, por lo que es mejor que se concentre en otros asuntos.

+0

+1 Gracias, eso ayuda :-) – nc3b

Cuestiones relacionadas