La función de biblioteca estándar fgets() tiene dos desventajas:

  • El segundo parámetro de la función es de tipo int
  • Deja un carácter de nueva línea al final en el búfer proporcionado

Hice una función simple similar a fgets() excluyendo las desventajas mencionadas anteriormente, para tratar de mejorar la eficiencia de uno de mis programas que busca las líneas de un archivo de texto y termina la matriz char en el carácter de nueva línea usando la función strcspn().

¿Pero es realmente más eficiente? ¿Hay alguna razón por la que la función de biblioteca estándar tenga ventaja sobre la siguiente implementación ingenua?

#include <stdio.h>


char *my_fgets(char *buf, size_t maxCount, FILE *stream);


int main(int argc, char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s [filename]", argv[0]);
    }

    FILE *fp;
    fp = fopen(argv[1], "r");

    if (!fp)
    {
        perror(argv[1]);
        return 1;
    }

    char buf[256];
    /*while (fgets(buf, sizeof(buf), fp))
    {
        buf[strcspn(buf, "\n")] = '\0';
        // . . .
        puts(buf);
    }*/

    while (my_fgets(buf, sizeof(buf) - 1, fp))
    {
        puts(buf);
    }

    return 0;
}


char *my_fgets(char *buf,
    size_t maxCount, FILE *stream)
{
    int ch;
    size_t n = 0;

    while ((ch = fgetc(stream)) != EOF)
    {
        if (ch == '\n' || n == maxCount)
        {
            break;
        }
        else
        {
            buf[n++] = ch;
        }
    }
    if (n == 0 && ch == EOF)
    {
        return NULL;
    }
    buf[n] = '\0';
    return buf;
}
2
machine_1 24 feb. 2018 a las 14:28

2 respuestas

La mejor respuesta

¿Es realmente más eficiente?

No, no es más eficiente en rendimiento. my_fgets(), en el mejor de los casos, podría tener una eficiencia similar, pero incluso eso es poco probable, ya que fgets() tiene acceso al código ensamblador elaborado y my_fgets() no @ Peter.

Sí, puede tener una eficiencia de codificación si el objetivo de la función es obtener una línea de entrada del usuario y no guardar el '\n' ya que esto es menos codificado que fgets() ... code_to_rid_\n()


Sin embargo, la eficiencia es secundaria a la funcionalidad.

my_fgets() tiene problemas menores con errores de entrada .

my_fgets() puede soltar caracteres, a diferencia de fgets() @ melpomene

El papel de size_t maxCount difiere en 1 del argumento similar de fgets().


Buen intento, ya que la biblioteca estándar fgets() tiene varias debilidades que obligan a los codificadores a intentar una mejor función de línea de entrada de bajo nivel.

0
chux - Reinstate Monica 24 feb. 2018 a las 12:39

Rendimiento

Su uso de fgetc() para buscar caracteres uno por uno garantiza que my_fgets() no puede competir con una solución optimizada con sensatez. Para obtener un buen rendimiento, necesita leer algunos caracteres en un búfer a través de la llamada al sistema read() y luego consumir los datos directamente desde ese búfer. Realizar una llamada de función completa para cada byte es una mucha sobrecarga.

Seguridad

fgets() no es seguro para uso general: dividirá largas filas. Esta condición generalmente no es anticipada (su código de ejemplo tampoco se ocupa de esto, por ejemplo) y generalmente conducirá a un comportamiento incorrecto cuando se proporcionen líneas de entrada muy largas a su programa. La presencia del \n final en realidad podría ayudarlo a lidiar con ese problema. Si desea un software seguro, debe manejar explícitamente el caso de búfer insuficiente al usar fgets() . Si no lo hace, definitivamente tiene un error esperando para atacar. Su implementación ni siquiera intenta abordar este problema.

Es por eso que sugeriría encarecidamente usar funciones de entrada que puedan manejar líneas de entrada de cualquier longitud. En sistemas compatibles con POSIX, eso sería getline(): esta función asigna un búfer lo suficientemente grande para usted, por lo que su único límite es la RAM disponible. Si getline() no está disponible en su plataforma, recomendaría volver a implementar su funcionalidad en lugar de una versión "algo mejor" de fgets().

1
cmaster - reinstate monica 24 feb. 2018 a las 18:38