¿Hay un getline
función que usa fread
(bloque E/S) en lugar de fgetc
(E/S de caracteres)?C: Lectura de un archivo de texto (con líneas de longitud variable) línea por línea usando fread()/fgets() en lugar de fgetc() (E/S de bloque frente a E/S de caracteres)
Hay una penalización de rendimiento al leer un archivo carácter por carácter a través de fgetc
. Creemos que para mejorar el rendimiento, podemos usar lecturas de bloque a través de fread
en el bucle interno de getline
. Sin embargo, esto introduce el efecto potencialmente indeseable de leer más allá del final de una línea. Al menos, esto requeriría la implementación de getline
para realizar un seguimiento de la parte "no leída" del archivo, que requiere una abstracción más allá de la semántica ANSI C FILE. ¡Esto no es algo que queremos implementar nosotros mismos!
Hemos perfilado nuestra aplicación, y el rendimiento lento se aísla al hecho de que estamos consumiendo archivos grandes carácter por carácter a través de fgetc
. El resto de los gastos generales en realidad tiene un costo trivial en comparación. Siempre estamos leyendo secuencialmente cada línea del archivo, de principio a fin, y podemos bloquear todo el archivo durante la lectura. Esto probablemente hace que sea más fácil implementar un fread
-based getline
.
Entonces, ¿existe una función getline
que utiliza fread
(E/S de bloque) en lugar de fgetc
(E/S de caracteres)? Estamos bastante seguros de que sí, pero si no, ¿cómo deberíamos implementarlo?
actualización Encontrado un artículo útil, Handling User Input in C, por Paul Hsieh. Es un enfoque basado en fgetc
, pero tiene una interesante discusión de las alternativas (a partir de lo mal gets
es decir, luego discutir fgets
):
Por otro lado, la réplica común de los programadores de C (incluso los que se consideran con experiencia) es decir que fgets() se debe utilizar como una alternativa. Por supuesto, por sí mismo, fgets() realmente no maneja la entrada del usuario per se. Además de tener una condición de terminación de cadena extraña (al encontrar \ n o EOF, pero no \ 0) el mecanismo elegido para la terminación cuando el búfer ha alcanzado su capacidad es detener bruscamente la operación fgets() y \ 0 terminarlo. Por lo tanto, si la entrada del usuario excede la longitud del búfer preasignado, fgets() devuelve un resultado parcial. Para lidiar con este programador tenemos un par de opciones; 1) simplemente trate con la entrada de usuario truncada (no hay manera de retroalimentar al usuario que la entrada ha sido truncada, mientras están proporcionando entrada) 2) Simular una matriz de caracteres cultivables y completarla con llamadas sucesivas a fgets (). La primera solución es casi siempre una solución muy pobre para la entrada de usuarios de longitud variable porque el buffer será inevitablemente demasiado grande la mayor parte del tiempo porque intenta capturar demasiados casos comunes y demasiado pequeño para casos inusuales. La segunda solución está bien, excepto que puede ser complicado implementarla correctamente. Tampoco se ocupa de fgets ' comportamiento impar con respecto a' \ 0 '.
Ejercicio deja al lector: Con el fin de determinar la cantidad de bytes que realmente leído por una llamada a fgets(), se podría tratar en los escaneos, tal como lo hace, por un '\ n' y saltar sobre cualquier '\ 0' mientras que no excede el tamaño pasado a fgets(). Explica por qué esto es insuficiente para la última línea de una transmisión.¿Qué debilidad de ftell() impide que solucione este problema por completo?
Ejercicio deja al lector: resolver el problema de determinar la longitud de los datos consumidos por fgets() sobrescribiendo todo el tampón con un valor distinto de cero entre cada llamada a fgets().
Así que con fgets() Nos quedamos con la opción de escribir un montón de código y vivir con una condición de terminación de línea que es incompatible con el resto de la biblioteca C, o que tenga un límite arbitrario. Si esto no es lo suficientemente bueno, entonces, ¿qué nos queda? scanf() mezcla el análisis con lectura de una manera que no se puede separar, y fread() leerá más allá del final de la cadena. En resumen, la biblioteca C nos deja sin nada. Estamos obligados a rodar el nuestro basado en la parte superior de fgetc() directamente. Así que vamos a darle una oportunidad.
Por lo tanto, funciona un getline
que se basa en fgets
(y no trunca la entrada) existen?
Para su nueva pregunta al final, sí, existe. Lo describí en mi respuesta. El artículo que ha mencionado menciona un problema con una línea final no terminada en una nueva línea; He hecho que esto no sea un problema al rellenar previamente el buffer con ''\ n'' y proporcionar una forma de detectar la condición. –
También tenga en cuenta que la solución de Paul Hsieh para usar 'fgetc' es muy mala. En las implementaciones modernas, debido al requisito de admitir el bloqueo en caso de que varios hilos accedan al mismo objeto 'FILE', usar' fgetc' será muy lento. Puede usar 'getc_unlocked' (pero esta es una función POSIX, no una función C estándar), pero incluso con una macroexpansión óptima de' getc_unlocked', la forma 'fgets' busca en el buffer' '\ n'' (es decir, usar 'memchr') será mucho más rápido que cualquier cosa que pueda hacer sin acceder al búfer interno. También tenga en cuenta que si tiene POSIX (2008), ya tiene 'getline'. –