Universidad Carlos III de Madrid

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2012 - Enero 2013

4. Funciones de entrada para leer strings de manera segura

Como hemos visto en el apartado anterior, las funciones gets y scanf usadas para leer cadenas de caracteres son propensas a errores, pues no controlan que el usuario haya introducido más caracteres que el que puede albergar el array definido para almacenarlos. Para ello, contamos con dos funciones que hacen segura la lectura de cadenas introducidas por el usuario, que son fgets y getline.

4.1. La función fgets

La sintaxis de la función fgets es:

#include <stdio.h>
char *fgets(char *s, int n, FILE *stream);

s referencia a un array de caracteres, que almacena lo que lee de un fichero apuntado por stream. n especifica el número máximo de elementos del array (caracteres). La función lee hasta n-1 caracteres, y añade el caracter nulo para que la cadena quede bien formada. Si no ha llegado hasta n-1 caracteres pero llega a un caracter de nueva línea, '\n', también para de leer y devuelve la cadena (con ese carácter de nueva línea incluido). Si no hay errores, la función devuelve el puntero s. Si encuentra el final de fichero (EOF) o existe algún error, devuelve null.

fgets es así más segura que gets, pero tienes que lidiar con un par de cosas. Una es que si has introducido más caracteres de los que ha podido leer, el resto de caracteres se quedan en el buffer de entrada (en stdin), así que tendrás que eliminarlos para que no sean leídos en el próximo fgets si no quieres. Lo segundo es que fgets incluye el carácter '\n' al final, así que tendrás que quitarlo.

4.2. La función getline

La librería GNU ofrece la función no estándar getline que hace sencilla la lectura de líneas:

#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream);

Esta función lee una línea entera de stream, almacenando el texto (incluyendo el carácter de nueva línea y el de terminación) en un buffer y almacenando la dirección del buffer en *lineptr. Antes de llamar a getline, tienes que colocar en *lineptr la dirección de un buffer de *n bytes de largo usando malloc. Si este buffer no es lo suficientemente grande como para albergar la frase leída, getline hace el buffer más grande usando realloc, actualizando la nueva dirección en *lineptr y el tamaño en *n. Si inicializas *lineptr a null y *n a cero, getline hace ese malloc por tí.

Si todo ha ido bien, *lineptr es un char * que apunta al texto que se ha leido. La función devuelve el número de caracteres leídos aunque sin contar el carácter '\0' de terminación (sí con el de nueva línea); si ha habido algún error o se alcanza final de fichero, devuelve -1.

Nota

Siempre que tengas que leer una línea de texto del teclado, hazlo pues con getline; te evitarás cualquier problema posterior.

El siguiente programa muestra cómo usar getline para leer una línea de texto desde teclado de manera segura. Intenta teclear más de 10 caracteres, verás que getline lo gestiona correctamente, independientemente del número de caracteres que teclees.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#define TAM_MAXIMO 10

int main(void)
{
  ssize_t bytes_leidos;
  size_t numero_bytes;
  //ssize_t y size_t son sinónimos de unsigned int
  char *cadena;

  puts("Por favor, introduce una línea de texto:\n");
  /* Puedes pasar a getline los argumentos inicializados: */
  //numero_bytes = TAM_MAXIMO;
  //cadena = (char *) malloc (numero_bytes + 1);
  //bytes_leidos = getline(&cadena, &numero_bytes, stdin);
  
  /*O bien, más sencillo, poner el número a 0 y la cadena a NULL, para que él mismo te haga
  la reserva necesaria*/
  numero_bytes = 0;
  cadena = NULL;
  bytes_leidos = getline(&cadena, &numero_bytes, stdin);

  if (bytes_leidos == -1)
  {
    puts("Error.");
  }
  else
  {
    puts("La línea es:");
    puts(cadena);
  }
  free(cadena);

  return 0;
}

Comprueba con estas preguntas si has entendido este documento.

  1. Indica la afirmación INCORRECTA respecto a la función getline:

    • Devuelve el número de caracteres leídos, incluyendo el caracter '\n' de fin de línea, pero no cuenta el '\0' de final de cadena.

    • Si el buffer de memoria que recibe como parámetro de entrada no tiene espacio suficiente, devuelve un error.

    • Si el puntero al buffer de memoria que se le pasa para guardar el resultado es NULL, reserva memoria internamente con un malloc.