¿Cómo puedo leer una cadena de entrada de longitud desconocida?

9 minutos de lectura

avatar de usuario
showkey

Si no sé cuánto tiempo es la palabra, no puedo escribir char m[6];,
La longitud de la palabra es tal vez diez o veinte. ¿Cómo puedo usar scanf obtener entrada desde el teclado?

#include <stdio.h>
int main(void)
{
    char  m[6];
    printf("please input a string with length=5\n");
    scanf("%s",&m);
    printf("this is the string: %s\n", m);
    return 0;
}

por favor ingrese una cadena con longitud = 5
entrada: hola
esta es la cadena: hola

  • usar combinación de reasignación de puntero

    – Pantera Rosa

    1 jun 2013 a las 10:00

  • Puedes soltar el & en scanf("%s",&m) ya que m ya es un puntero al primer elemento de m[] en esta expresión.

    – Jens

    1 de junio de 2013 a las 11:10

avatar de usuario
AZULPIXY

Ingrese mientras asegura un área dinámicamente

P.EJ

#include <stdio.h>
#include <stdlib.h>

char *inputString(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
    char *str;
    int ch;
    size_t len = 0;
    str = realloc(NULL, sizeof(*str)*size);//size is start size
    if(!str)return str;
    while(EOF!=(ch=fgetc(fp)) && ch != '\n'){
        str[len++]=ch;
        if(len==size){
            str = realloc(str, sizeof(*str)*(size+=16));
            if(!str)return str;
        }
    }
    str[len++]='\0';

    return realloc(str, sizeof(*str)*len);
}

int main(void){
    char *m;

    printf("input string : ");
    m = inputString(stdin, 10);
    printf("%s\n", m);

    free(m);
    return 0;
}

  • multiplicando por sizeof(char)? Puaj.

    – Jens

    1 de junio de 2013 a las 11:06

  • @Jens Pfff, eso probablemente se optimizará. No hay problemas. Pero si tuviera que hacer una búsqueda y reemplazo global de char con wchar_testa solución aún funcionaría, a diferencia de otras soluciones, ¡que necesitarían más retoques!

    – Señor Lister

    1 de junio de 2013 a las 11:12


  • @MrLister Por eso, la forma correcta, si es que la hay, es multiplicar con sizeof (*str) así que ni siquiera tienes que editar la multiplicación cuando cambia el tipo.

    – Jens

    1 de junio de 2013 a las 12:11


  • Definitivamente es el caso que str[len]='\0'; return realloc(str, len); dará como resultado que el terminador sea descartado. creo que quisiste decir str[len++] = '\0'.

    – sh1

    1 de junio de 2013 a las 12:12

  • @germanfr No. Dije sobre realloc(NULL, sizeof(char)*size); the same as malloc(sizeof(char) * size). no digo lo mismo de malloc y realloc.

    – PIXY AZUL

    16/10/2016 a las 15:01

avatar de usuario
Sr. Lister

Con las computadoras de hoy, puede salirse con la suya asignando cadenas muy grandes (cientos de miles de caracteres) sin apenas hacer mella en el uso de RAM de la computadora. Así que no me preocuparía demasiado.

Sin embargo, en los viejos tiempos, cuando la memoria era escasa, la práctica común era leer cadenas en fragmentos. fgets lee hasta un número máximo de caracteres de la entrada, pero deja intacto el resto del búfer de entrada, por lo que puede leer el resto como quiera.

en este ejemplo, leo en fragmentos de 200 caracteres, pero, por supuesto, puede usar el tamaño de fragmento que desee.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* readinput()
{
#define CHUNK 200
   char* input = NULL;
   char tempbuf[CHUNK];
   size_t inputlen = 0, templen = 0;
   do {
       fgets(tempbuf, CHUNK, stdin);
       templen = strlen(tempbuf);
       input = realloc(input, inputlen+templen+1);
       strcpy(input+inputlen, tempbuf);
       inputlen += templen;
    } while (templen==CHUNK-1 && tempbuf[CHUNK-2]!='\n');
    return input;
}

int main()
{
    char* result = readinput();
    printf("And the result is [%s]\n", result);
    free(result);
    return 0;
}

Tenga en cuenta que este es un ejemplo simplificado sin verificación de errores; en la vida real, deberá asegurarse de que la entrada esté bien verificando el valor de retorno de fgets.

También tenga en cuenta que al final de la rutina readinput, no se desperdician bytes; la cadena tiene el tamaño de memoria exacto que necesita tener.

  • Necesita manejo de errores para realloc() regresando NULL (y, en consecuencia, para readinput() regresando NULL).

    – sh1

    1 de junio de 2013 a las 12:29


  • Necesidad de comprobar el valor de retorno de fgets()de lo contrario, el código puede entrar en un bucle infinito.

    – chux – Reincorporar a Monica

    6 de diciembre de 2015 a las 20:18

  • Creo que hay un problema con esto en la primera realloc (realloc cuando la entrada es NULL). Esto podría apuntar a una memoria arbitraria y, por lo tanto, es posible que strcat no tenga el resultado deseado (es decir, la entrada debe ser solo el contenido del búfer). En cambio, en lugar de tratar de almacenar una cadena de longitud templen que se ha asignado, intenta almacenar una cadena de strlen (datos arbitrarios) + templen, y da un error de “corrupción de memoria malloc ()”.

    –Brendan Hart

    15 oct 2019 a las 17:47


  • @BrendanHart Aw, nadie vio eso en seis años. Se solucionó haciendo un strcpy en lugar de un strcat.

    – Señor Lister

    15 oct 2019 a las 19:03

  • Para perder el avance de línea, agregue tempbuf[strcspn(tempbuf, "\n")] = 0; después fgets línea.

    – No

    22 de noviembre de 2020 a las 17:25

avatar de usuario
sh1

solo he visto uno sencillo forma de leer una cadena arbitrariamente larga, pero nunca la he usado. Creo que va así:

char *m = NULL;
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
    fprintf(stderr, "That string was too long!\n");
else
{
    printf("this is the string %s\n",m);
    /* ... any other use of m */
    free(m);
}

Él m Entre % y s dice scanf() para medir la cadena y asignarle memoria y copiar la cadena en ella, y almacenar la dirección de esa memoria asignada en el argumento correspondiente. Una vez que hayas terminado con él, tienes que free() eso.

Esto no es compatible con todas las implementaciones de scanf()aunque.

Como han señalado otros, la solución más fácil es establecer un límite en la longitud de la entrada. Si aún desea utilizar scanf() entonces puedes hacerlo de esta manera:

char m[100];
scanf("%99s",&m);

Tenga en cuenta que el tamaño de m[] debe ser al menos un byte mayor que el número entre % y s.

Si la cadena ingresada es más larga que 99, los caracteres restantes esperarán a ser leídos por otra llamada o por el resto de la cadena de formato pasada a scanf().

Generalmente scanf() no se recomienda para manejar la entrada del usuario. Es mejor aplicarlo a archivos de texto estructurados básicos que fueron creados por otra aplicación. Incluso entonces, debe tener en cuenta que la entrada podría no estar formateada como esperaba, ya que alguien podría haber interferido con ella para intentar romper su programa.

  • Tenga en cuenta que "%ms" no es C estándar — probablemente sea una extensión POSIX o una extensión GNU.

    – Tim Cas

    12 de febrero de 2015 a las 21:36

  • @TimČas: Es parte de Posix 2008, que es un estándar. Había una extensión GNU similar anterior y una extensión BSD similar; el estándar Posix está destinado a unificar las diversas implementaciones. Es muy posible que encuentre su camino hacia un futuro estándar C.

    – rico

    12 de febrero de 2015 a las 22:37

Hay una nueva función en el estándar C para obtener una línea sin especificar su tamaño. getline La función asigna la cadena con el tamaño requerido automáticamente, por lo que no es necesario adivinar el tamaño de la cadena. El siguiente código demuestra el uso:

#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    while ((read = getline(&line, &len, stdin)) != -1) {
        printf("Retrieved line of length %zu :\n", read);
        printf("%s", line);
    }

    if (ferror(stdin)) {
        /* handle error */
    }

    free(line);
    return 0;
}

avatar de usuario
Nobilis

Si puedo sugerir un enfoque más seguro:

Declare un búfer lo suficientemente grande como para contener la cadena:

char user_input[255];

Obtener la entrada del usuario en un a salvo camino:

fgets(user_input, 255, stdin);

Una forma segura de obtener la entrada, el primer argumento es un puntero a un búfer donde se almacenará la entrada, el segundo la entrada máxima que debe leer la función y el tercero es un puntero a la entrada estándar, es decir, donde viene la entrada del usuario desde.

La seguridad en particular proviene del segundo argumento que limita cuánto se leerá, lo que evita el desbordamiento del búfer. También, fgets se encarga de la terminación nula de la cadena procesada.

Más información sobre esa función. aquí.

EDITAR: si necesita hacer algún formato (por ejemplo, convertir una cadena en un número), puede usar atoi una vez que tenga la entrada.

  • pero el OP pregunta que no sabe cuánto va a ingresar y si quiere ingresar al azar con> 255

    – Pantera Rosa

    1 de junio de 2013 a las 10:01

  • OMI fgets(user_input, sizeof user_input, stdin); Es más seguro.

    – chux – Reincorporar a Monica

    06/12/2015 a las 19:56

  • @chux-ReinstateMonica Hasta que use un puntero en lugar de una matriz;)

    – klutt

    26 de febrero de 2021 a las 14:45

  • pero digamos que escribes un caparazón. ¿Realmente desea limitar a 255 o cualquier valor fijo? La primera respuesta parece mejor en el manejo de cadenas de caracteres desconocidos en tiempo de compilación de cadenas de tamaño.

    – Et7f3XIV

    30 de mayo de 2021 a las 9:41

Versión más segura y rápida (doble capacidad):

char *readline(char *prompt) {
  size_t size = 80;
  char *str = malloc(sizeof(char) * size);
  int c;
  size_t len = 0;
  printf("%s", prompt);
  while (EOF != (c = getchar()) && c != '\r' && c != '\n') {
    str[len++] = c;
    if(len == size) str = realloc(str, sizeof(char) * (size *= 2));
  }
  str[len++]='\0';
  return realloc(str, sizeof(char) * len);
}

  • pero el OP pregunta que no sabe cuánto va a ingresar y si quiere ingresar al azar con> 255

    – Pantera Rosa

    1 de junio de 2013 a las 10:01

  • OMI fgets(user_input, sizeof user_input, stdin); Es más seguro.

    – chux – Reincorporar a Monica

    06/12/2015 a las 19:56

  • @chux-ReinstateMonica Hasta que use un puntero en lugar de una matriz;)

    – klutt

    26 de febrero de 2021 a las 14:45

  • pero digamos que escribes un caparazón. ¿Realmente desea limitar a 255 o cualquier valor fijo? La primera respuesta parece mejor en el manejo de cadenas de caracteres desconocidos en tiempo de compilación de cadenas de tamaño.

    – Et7f3XIV

    30 de mayo de 2021 a las 9:41

Tome un puntero de carácter para almacenar la cadena requerida. Si tiene alguna idea sobre el tamaño posible de la cadena, use la función

char *fgets (char *str, int size, FILE* file);`

de lo contrario, también puede asignar memoria en tiempo de ejecución usando malloc() función que proporciona dinámicamente la memoria solicitada.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad