Hay un getline función que utiliza fread (bloque I/O) en lugar de fgetc (I/O)?

Hay una penalización de rendimiento a la lectura de un archivo de carácter por carácter a través de fgetc. Pensamos que para mejorar el rendimiento, se puede utilizar el bloque de lecturas a través de fread en el bucle interno de getline. Sin embargo, esto introduce la potencialmente indeseable efecto de la lectura más allá del final de una línea. Al menos, esto requeriría la aplicación de getline de seguir la pista de los «no leídos» parte del archivo, que requiere de una abstracción más allá de la ANSI C semántica de ARCHIVO. Esto no es algo que queremos aplicar a nosotros mismos!

Hemos perfilado de nuestra aplicación, y el rendimiento lento es aislado el hecho de que estamos consumiendo archivos de gran tamaño, carácter por carácter a través de fgetc. El resto de la sobrecarga en realidad tiene un trivial costo por comparación. Siempre estamos leyendo de forma secuencial cada línea del archivo, de principio a fin, y podemos bloquear el archivo completo para la duración de la lectura. Esto probablemente hace un freadbasado en getline más fácil de implementar.

Así, hace un getline función que utiliza fread (bloque I/O) en lugar de fgetc (I/O) existen? Estamos bastante seguros de que no, pero si no, ¿cómo debemos hacerlo?

Actualización Encontrado un artículo útil, el Manejo de la Entrada de Usuario en C, por Pablo Hsieh. Es un fgetcenfoque basado en, pero tiene una interesante discusión de las alternativas (a partir de lo mal gets es, luego de discutir fgets):

Por otro lado, el común de la retorta de los programadores de C (incluso aquellos que se consideren con experiencia) es decir que fgets() debe ser utilizado como una alternativa. Por supuesto, por sí mismo, fgets() realmente no controlar la entrada de usuario en sí. Además de tener una extraña cadena de condición de terminación (en caso de encontrarse con \n o EF, pero no \0) el mecanismo elegido para la terminación cuando el buffer ha alcanzado su capacidad máxima es simplemente detener abruptamente la fgets() operación y \0 terminarlo. Así que si la entrada del usuario excede la longitud de la preasignados buffer, fgets() devuelve un resultado parcial. Para lidiar con esto los programadores tienen un par de opciones; 1) se limita a tratar con trunca entrada de usuario (no hay manera de retroalimentar al usuario de que la entrada ha sido truncado, mientras proveen de entrada) 2) Simular una cultivable matriz de caracteres y lo relleno con sucesivas llamadas a fgets(). La primera solución, es casi siempre una mala solución para la variable longitud de la entrada del usuario, porque el búfer inevitablemente va a ser demasiado grande, la mayoría del tiempo debido a que su tratando de capturar demasiados los casos ordinarios, y demasiado pequeño para casos excepcionales. La segunda solución es bien excepto que puede ser complicado de aplicar correctamente. Ni ofertas con fgets’ extraño comportamiento con respecto a ‘\0’.

Ejercicio izquierda para el lector: En el fin de determinar cuántos bytes fue muy leído por una llamada a fgets(), uno puede tratar mediante el análisis, tal como lo hace, por un ‘\n’ y saltar por encima de cualquier ‘\0’ mientras que no exceda el tamaño pasa a fgets(). Explique por qué esto es insuficiente para la última línea de un arroyo. Qué debilidad de ftell() impide abordar este problema por completo?

Ejercicio izquierda para el lector: Resolver el problema de la determinación de la longitud de los datos consumidos por fgets() sobrescribiendo todo el búfer 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 línea de terminación de la condición de lo que es inconsistente con el resto de la biblioteca de C, o tener un arbitrario de corte. Si esto no es suficientemente bueno, entonces, ¿qué nos queda? scanf() mezclas de análisis con la lectura de una manera que no pueden separarse, y fread() va a leer más allá del final de la cadena. En resumen, la biblioteca de C nos deja con nada. Estamos obligados a rodar nuestro propio basado en la parte superior de fgetc() directamente. Así que vamos a darle un tiro.

Así, hace un getline función que se basa en fgets (y no truncar la entrada) existen?

Para su nueva pregunta al final, sí, existe. He comentado en mi respuesta. El artículo que he citado menciona un problema con un final no newline terminada en línea; he hecho esto no es un problema por el pre-llenado el buffer con '\n' y proporcionar un método para detectar la enfermedad.
También tenga en cuenta que Pablo Hsieh la solución para usar fgetc es muy malo. En las implementaciones modernas, debido a la necesidad de apoyo de bloqueo en caso de múltiples hilos de acceso a la misma FILE objeto, utilizando fgetc va a ser muy lento. Usted puede utilizar getc_unlocked (pero esta es una función POSIX, y no un estándar de la función de C), pero incluso con un óptimo de expansión de macro de getc_unlocked, la forma en que fgets busca en el búfer para '\n' (es decir, utilizando memchr) será muchas veces más rápido que cualquier cosa que usted puede hacer sin tener acceso a el buffer interno. También tenga en cuenta que si usted tiene POSIX (2008), usted tiene getline ya.

OriginalEl autor Julienne Goldberg | 2010-12-10

2 Comentarios

  1. 5

    No uso fread. Uso fgets. Aprovecho esta es una tarea/classproject problema por lo que no estoy de proporcionar una respuesta completa, pero si dices que no, te voy a dar más consejos. Es definitivamente posible para proporcionar el 100% de la semántica de GNU estilo getline, incluso la incorporación de bytes nulos, el uso puramente fgets, pero requiere cierta inteligencia.

    OK, actualizar ya que esto no es la tarea:

    • memset su búfer de '\n'.
    • Uso fgets.
    • Uso memchr para encontrar el primer '\n'.
    • Si no '\n' se encuentra, la línea es más larga que su búfer. Englarge el buffer, llene la parte nueva con '\n', y fgets en la parte nueva, repetir según sea necesario.
    • Si el carácter que sigue a '\n' es '\0', entonces fgets terminado debido a la presencia de llegar al final de una línea.
    • De lo contrario, fgets terminado debido a alcanzar EF, la '\n' queda de su memset, el carácter anterior es el nulo de terminación que fgets escribió, y el personaje antes de que es el último carácter de los datos reales de lectura.

    Puede eliminar la memset y uso strlen en lugar de memchr si usted no se preocupan por el apoyo a líneas con los valores nulos (de cualquier manera, la nula voluntad de no terminar la lectura, que va a ser parte de su lectura en línea).

    También hay una manera de hacer lo mismo con fscanf y la "%123[^\n]" especificador (donde 123 es su límite de búfer), lo que le da la flexibilidad para parar en el no-caracteres de salto de línea (ala GNU getdelim). Sin embargo, es probablemente lento a menos que su sistema tiene un muy elegante scanf aplicación.

    Esto no es tarea… 🙂 ¿Cómo sugieren el uso de fgets? El uso de un crecimiento capaz de matriz de caracteres y rellenar con las sucesivas llamadas a fgets parece complicado de aplicar correctamente. También, entiendo que fgets termina tras el encuentro con ‘\n’ o EF, pero no ‘\0’. Esto no es un problema para nuestros archivos, a pesar de.
    Un menor de edad agujero: Después de usar char s[5]; memset(s, '\n', sizeof s); fgets(s, sizeof s, ...); en un archivo con 3 bytes «xyz» conduce a «xyz\0\n» en s. Encontrar el primer '\n' está bien, pero la comprobación de la siguiente carácter de la UB. Sugerir la adición de «Si ‘\n’ en el último lugar, a continuación, fgets terminado debido a que hasta la última línea en el archivo.», a continuación, vaya a «Si el carácter que sigue …»
    Me pregunto por qué tantas cadena de funciones relacionadas con la relativamente inútil valores de retorno? Código que llama a strcat y fgets menudo se necesita para encontrar el último carácter escrito-algo así como el código de estas funciones ya se han conocido. Yo no puedo pensar en ninguna utilidad para el valor de retorno de las funciones ya implementadas.

    OriginalEl autor R..

  2. 1

    No hay una gran diferencia de rendimiento entre fgets y fgetc/setvbuf.
    Probar:

    int c;
    FILE *f = fopen("blah.txt","r");
    setvbuf(f,NULL,_IOLBF,4096); /* !!! check other values for last parameter in your OS */
    while( (c=fgetc(f))!=EOF )
    {
      if( c=='\n' )
        ...
      else
        ...
    } 

    OriginalEl autor user411313

Dejar respuesta

Please enter your comment!
Please enter your name here