elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Recuerda que debes registrarte en el foro para poder participar (preguntar y responder)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación C/C++ (Moderadores: Eternal Idol, Littlehorse, K-YreX)
| | |-+  El bendito stdin...
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: El bendito stdin...  (Leído 2,904 veces)
zShackra

Desconectado Desconectado

Mensajes: 59


Ver Perfil WWW
El bendito stdin...
« en: 24 Noviembre 2014, 00:28 am »

Hace unos días publiqué un tema donde al parecer se limpiaba el estándar de entrada de forma correcta y así evitábamos comportamientos indeseados en una lectura desde este, de forma portable (independiente del sistema operativo).

Hasta ahora no he tenido problemas al leer desde este con cualquiera de las siguientes funciones (aunque prefiero la segunda):

Código
  1. size_t leerLinea(char *arr, size_t tam)
  2. {
  3. int c = EOF;
  4. size_t i;
  5.  
  6. if (!tam)
  7. return 0;
  8.  
  9. for (i = 0; i < tam - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
  10. arr[i] = c;
  11.  
  12. arr[i] = '\0';
  13.  
  14. while (c != '\n' && c != EOF)
  15. c = getchar();
  16.  
  17. return i;
  18. }
  19.  
  20. size_t leerLinea(char *arr, size_t tam)
  21. {
  22. int c = (tam == 1) ? EOF : 0;
  23.  
  24. if (fgets(arr, tam, stdin) == NULL)
  25. return 0;
  26.  
  27. while (arr[strlen(arr) - 1] != '\n' && c != '\n' && c != EOF)
  28. c = getchar();
  29.  
  30. if (arr[strlen(arr) - 1] == '\n')
  31. arr[strlen(arr) - 1] = '\0';
  32.  
  33. return strlen(arr);
  34. }

Ambas leen tam - 1 caracteres y descartan todo lo que haya luego de los mismos, más sin embargo, aún tengo una duda... la cual creo que haría inútil las funciones anteriores.

Según he leído en las redes, stdin es un (FILE *) stream que, al parecer, sólo se encuentra en memoria; existe un módulo kernel que lo controla y le da un espacio en esta (independientemente del sistema operativo) para que allí, se almacene la información ingresada detrás de cada salto de línea ('\n').

Bien, si todo esto es cierto, me pregunto... ¿cómo acceder a esa zona de memoria? es decir, cómo obtengo esa dirección... la requiero para jugar un rato con la stdin, y ver si me puedo deshacer de las funciones anteriores, pues si logro acceder a esa zona de memoria, tras cada lectura de entrada (con un simple fgets() sin temor a dejar basura) puedo obtener lo que deseo; lo que no, simplemente lo re-escribo con ceros y así "limpio" de una forma más directa la entrada, o bien... almaceno todo lo que el usuario ha ingresado por la terminal y luego doy formato (mi real propósito)...

Lo que busco es, en síntesis, crear una función que reciba todo lo que se ha ingresado por la terminal sin desechar nada, y para eso... requiero saber la ubicación del búfer de stdin para así, como comenté anteriormente, almacenar todo lo que se ingrese... lo deseo hacer por ocio más que nada, sé que sí es posible, ya que la plataforma .NET lo hace... si declaramos un string, no es necesario definir su tamaño ni tampoco el tamaño de la cadena a leer, la plataforma .NET re-asigna memoria hasta lograr almacenar lo que el usuario ha ingresado, en caso de no haber memoria suficiente, lanza una excepción...

Bien, es eso a lo que quisiera llegar... porque sinceramente da pena el manejo de cadenas en C (aunque en C++ se ha mejorado bastante, no es tan eficiente como la plataforma .NET)...

Ya sé que no tiene mucho sentido hacer algo semejante, pues si al caso vamos, si no estamos escribiendo un editor de textos ni nada por el estilo, no es necesario un manejo dinámico de cadenas en C, pues simplemente se usa una de las funciones anteriores para obtener lo que el programa requiere, no lo que el usuario demande... pero mi curiosidad es más grande y como .NET parte de C++ (para C#), y C++ de C, supongo es lógicamente posible. (No quiero reinventar la rueda, simplemente quiero curiosear).

Cualquier información/sugerencia/crítica será bien recibida.

EDITO:

Tal parece que... leyendo un poco sobre punteros (aún no llego a dicho capítulo en el libro de K&R), logré escribir la siguiente función, la cual lee toda la entrada de teclado hasta el salto de línea, y reasigna memoria según necesite cada vez que se llenen 4kB de esta (tengo entendido que Windows y Linux dividen la memoria virtual en páginas de 4kB), es lo que estaba buscando... así pues, me evito problemas con dejar basura en la entrada y obtengo todo lo que el usuario ha ingresado, ya luego doy formato a esa entrada...

Código
  1. char* leerLinea()
  2. {
  3. int c;
  4. size_t p4kB = 4096, i = 0;
  5. void *newPtr = NULL;
  6. char *ptrString = malloc(p4kB * sizeof (char));
  7.  
  8. while (ptrString != NULL && (c = getchar()) != '\n' && c != EOF)
  9. {
  10. if (i == p4kB * sizeof (char))
  11. {
  12. p4kB += 4096;
  13. if ((newPtr = realloc(ptrString, p4kB * sizeof (char))) != NULL)
  14. ptrString = (char*) newPtr;
  15. else
  16. {
  17. free(ptrString);
  18. return NULL;
  19. }
  20. }
  21. ptrString[i++] = c;
  22. }
  23.  
  24. if (ptrString != NULL)
  25. {
  26. ptrString[i] = '\0';
  27. ptrString = realloc(ptrString, strlen(ptrString) + 1);
  28. }
  29. else return NULL;
  30.  
  31. return ptrString;
  32. }

Como se puede observar, la función devuelve un puntero donde se ubica la lectura de la entrada del usuario hasta encontrar EOF o un salto de línea, el cual es reemplazado con un carácter nulo (fin de cadena). En caso de no poder reasignar memoria, libera la utilizada hasta el momento y retorna un puntero nulo. Finalmente si logra leer todos los caracteres y sobra memoria de la reservada, reasigna memoria para liberar todo el espacio sobrante.

Agradecería usuarios más experimentados me dieran sugerencias para modificar el código, no veo más formas de optimizarlo ni hacerlo más seguro... Ya comprobé que sí es POSIX. Y a mis exigencias, cumple con lo que necesito... espero que a alguien le sirva, pero si aún hay modificaciones que se puedan aplicar para mejorar el código, no duden en hacérmelas saber.


« Última modificación: 25 Noviembre 2014, 08:10 am por zShackra » En línea

ivancea96


Desconectado Desconectado

Mensajes: 3.412


ASMático


Ver Perfil WWW
Re: El preciado estándar de entrada...
« Respuesta #1 en: 24 Noviembre 2014, 01:04 am »

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682079(v=vs.85).aspx


En línea

zShackra

Desconectado Desconectado

Mensajes: 59


Ver Perfil WWW
Re: El preciado estándar de entrada...
« Respuesta #2 en: 24 Noviembre 2014, 02:45 am »

Olvidé mencionar que estoy buscando información para un código compatible con POSIX... no gusto de enfocarme en un sólo sistema.

P.D.: Ya que logré perfeccionar la última función (char *leerLinea()) y cumple mis exigencias, y como nadie pudo aportar algo... agradezco cierren el tema, si desean.
« Última modificación: 25 Noviembre 2014, 08:30 am por zShackra » En línea

rir3760


Desconectado Desconectado

Mensajes: 1.639


Ver Perfil
Re: El bendito stdin...
« Respuesta #3 en: 26 Noviembre 2014, 05:16 am »

Algunos comentarios:

* No es necesario inicializar la variable "newPtr" a NULL ya que en su primer uso almacenas el valor de retorno de realloc.
* No es necesaria la conversión explicita en la sentencia:
Código
  1. ptrString = (char *) newPtr;
* El valor de sizeof(char) siempre es igual a uno, no es necesario indicarlo.
* En la comparación dentro del bucle debes utilizar "i == p4kB - 1", esto para reservar el ultimo elemento del bloque para el '\0'.
* La única forma en que la variable "ptrString" puede almacenar NULL es si la primera reserva de memoria falla (ya que las siguientes con realloc las manejas dentro del bucle), por ende si colocas el bucle dentro de una sentencia condicional if te ahorras varias comprobaciones.
* No es necesario utilizar strlen para conocer la longitud de la cadena ya que ese valor ya lo tienes almacenado en la variable "i".

Con los cambios:
Código
  1. char *leerLinea(void)
  2. {
  3.   int c;
  4.   size_t p4kB = 4096, i = 0;
  5.   void *newPtr;
  6.   char *ptrString;
  7.  
  8.   if ((ptrString = malloc(p4kB)) != NULL){
  9.      while ((c = getchar()) != '\n' && c != EOF){
  10.         if (i == p4kB - 1){
  11.            p4kB += 4096;
  12.  
  13.            if ((newPtr = realloc(ptrString, p4kB)) != NULL)
  14.               ptrString = newPtr;
  15.            else {
  16.               free(ptrString);
  17.               return NULL;
  18.            }
  19.         }
  20.  
  21.         ptrString[i++] = c;
  22.      }
  23.  
  24.      ptrString[i++] = '\0';
  25.      ptrString = realloc(ptrString, i);
  26.   }
  27.  
  28.   return ptrString;
  29. }

Un saludo
En línea

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language
zShackra

Desconectado Desconectado

Mensajes: 59


Ver Perfil WWW
Re: El bendito stdin...
« Respuesta #4 en: 26 Noviembre 2014, 09:32 am »

* No es necesario inicializar la variable "newPtr" a NULL ya que en su primer uso almacenas el valor de retorno de realloc.

Lo hacía bajo prácticas ortodoxas... prefiero que se inicialice como NULL a cualquier otra cosa, sin embargo al final es lo mismo, no hay riesgo de acceder a zonas indebidas.

* No es necesaria la conversión explicita en la sentencia

Así es, se me olvidó que era redundante en C, por eso no hice conversiones en las demás asignaciones.

* El valor de sizeof(char) siempre es igual a uno, no es necesario indicarlo.

Sí, tienes razón; me he confundido con comentarios de otros usuarios en la red...

* En la comparación dentro del bucle debes utilizar "i == p4kB - 1", esto para reservar el ultimo elemento del bloque para el '\0'.

Ese tipo de fallas son las que precisamente buscaba.

Así pues, tomando algunas de tus correcciones la función luce de la siguiente manera:

Código
  1. char* leerLinea(void)
  2. {
  3. int c;
  4. size_t p4kB = 4096, i = 0;
  5. void *newPtr = NULL;
  6. char *ptrString = NULL;
  7.  
  8. if ((ptrString = malloc(p4kB)) != NULL)
  9. {
  10. while ((c = getchar()) != '\n' && c != EOF)
  11. {
  12. if (i + 1 == p4kB)
  13. {
  14. if ((newPtr = realloc(ptrString, (p4kB += 4096))) != NULL)
  15. ptrString = newPtr;
  16. else
  17. {
  18. free(ptrString);
  19. return NULL;
  20. }
  21. }
  22.  
  23. ptrString[i++] = c;
  24. }
  25.  
  26. *((ptrString = realloc(ptrString, i + 1)) + i) = '\0';
  27. }
  28.  
  29. return ptrString;
  30. }

Gracias por tu colaboración, cualquier otro detalle, será bien recibido.
« Última modificación: 26 Noviembre 2014, 09:53 am por zShackra » En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
Sber si hay datos en stdin ?
Programación C/C++
lDanny 6 3,138 Último mensaje 25 Mayo 2010, 11:39 am
por lDanny
bendito via c3(problemas con software). Pido sugerencias
Software
chillinfart 0 2,085 Último mensaje 26 Agosto 2010, 05:52 am
por chillinfart
problemonnn con el bendito (initramfs)
GNU/Linux
rub'n 2 4,633 Último mensaje 27 Diciembre 2010, 23:16 pm
por fuenteRea
[C] Limpiando la stdin correctamente...
Programación C/C++
zShackra 9 4,905 Último mensaje 12 Noviembre 2014, 20:44 pm
por zShackra
Definición de stdin en C
Programación C/C++
fafafa01 8 4,457 Último mensaje 10 Septiembre 2017, 12:39 pm
por ivancea96
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines