[C] Limpiando la stdin correctamente...

Iniciado por zShackra, 12 Noviembre 2014, 04:12 AM

0 Miembros y 4 Visitantes están viendo este tema.

zShackra

Hola, he creado una función personalizada de la función getline() que aparece en el libro de K&R (en su primer capítulo)... quisiera me comentaran su comportamiento bajo sistemas Un*x:

int getLine(char buffer[], int max)
{
int c = EOF, i;

if (!max)
return 0;

for (i = 0; i < max - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
buffer[i] = c;

buffer[i] = '\0';

while (c != '\n' && c != EOF)
c = getchar();

return (i);
}


Para quien no la comprenda, aunque es bastante simple, simplemente se leen hasta max - 1 caracteres y se asignan a buffer[], la diferencia con fgets() es que dicha función nos puede dejar basura en la stdin si el usuario introduce una cadena más larga de lo esperado y así, al momento de intentar leer con cualquier otra función desde la stdin, vamos a leer los caracteres que sobraron de la lectura anterior.

Lógicamente se pueden limpiar con una macro:

#define stdinClr() int c; while ((c = getchar()) != '\n' && c != EOF);

Tal y como lo hago en getLine(), pero si usáramos siempre fgets() + stdinClr(), en caso que el usuario no ingresara más caracteres de lo esperado, fgets() realizaría su trabajo correctamente y el programa esperaría la entrada de un salto de línea o fin de archivo... lo cual es un comportamiento indeseado.

Por lo que la función getLine() considero es la más apropiada para obtener una cadena de n - 1 caracteres y así trabajar con la misma de forma más segura... con la stdin, claramente.

Ergo, quisiera saber si funciona en otros sistemas fuera de la familia NT. Si alguien tiene la amabilidad... lo que deseo es deshacerme de fgets() + stdClr() para con la stdin y hacerlo de forma portable.

eferion

Cita de: zShackra en 12 Noviembre 2014, 04:12 AM
Hola, he creado una función personalizada de la función getline() que aparece en el libro de K&R (en su primer capítulo)... quisiera me comentaran su comportamiento bajo sistemas Un*x:

En eso no te puedo ayudar demasiado, mi entorno de trabajo actual, para bien o para mal, es Windows.

Cita de: zShackra en 12 Noviembre 2014, 04:12 AM
Lógicamente se pueden limpiar con una macro:

Las macros que inyectan código hay que manejarlas con cuidado... en este caso, si tienes otra variable que se llama "c" dentro de la función en la que incluyas esta macro vas a tener problemas de compilación.

A mi modo de ver es mejor crear una función específica... pero no deja de ser mi opinión.

daryo

#2
funciona en linux.Solo un pequeñp cambio:
buffer[i++] = '\0';

y no le vi utilidad a esto:
if (c != '\n' && c != EOF)
while ((c = getchar()) != '\n' && c != EOF);


lo quite y funciona igual sin problemas de buffer(al menos en linux compilador gcc ) xD
buenas

vangodp

Y que te parece mi scanf cañero que te ajora to lo anterior?
char cadena[100];
scanf ( "%99[^\n]%*c", cadena );

con esta criatura lees un string de maximo 99 chars, te mete el \0 al final, evita el molesto enter y también el tener que limpiar ningún buffer XDDD  :silbar:
A ver que opinan  ;-)

leosansan

#4
Cita de: vangodp en 12 Noviembre 2014, 15:39 PM
Y que te parece mi scanf cañero que te ajora to lo anterior?
char cadena[100];
scanf ( "%99[^\n]%*c", cadena );

con esta criatura lees un string de maximo 99 chars, te mete el \0 al final, evita el molesto enter y también el tener que limpiar ningún buffer XDDD  :silbar:
A ver que opinan  ;-)

¿Y si metes 120 caracteres dónde crees que se van a situar los restantes a 99?. ¡¡¡¡ EN EL BUFFER ¡¡¡. Por lo tanto, en ese caso queda por limpiarlo si te excedes en el tamaño.  ;)

Es más, en ese caso el "%*c" se comería el primer caracter en exceso, quedando en el buffer los restantes y "acompañados" del "\n" con lo que si hay otro scanf posterior se lo saltaría.

¡¡¡¡ Saluditos! ..... !!!!



eferion

mi opinión es que la librería estándar podría estar un poco más completa en este apartado.

daryo

Cita de: eferion en 12 Noviembre 2014, 16:15 PM
mi opinión es que la librería estándar podría estar un poco más completa en este apartado.

si :/ siempre he dicho que c/c++ en cuanto a manejo de cadenas tiene herramientas demasiado basicas pero cuando lo digo los amantes de c/c++ sacan sus garras xD
buenas

eferion

Cita de: daryo en 12 Noviembre 2014, 16:19 PM
si :/ siempre he dicho que c/c++ en cuanto a manejo de cadenas tiene herramientas demasiado basicas pero cuando lo digo los amantes de c/c++ sacan sus garras xD

A mi me gusta bastante tanto C como C++, opino que son muy potentes y versátiles, pero me chocan cosas como...

* Que la librería estándar de C de un soporte tan pobre a algo tan básico como la consola.
* Que existan hasta tres formas diferentes de sobrecargar los mismos operadores.
* Que a estas alturas de la vida, C++ no admita templates abstractos
* Que no se vaya limitando el uso de las macros en pro de mejores herramientas.

Cada punto tiene su por qué, ya me conozco los motivos y no hace falta que me las recordéis... solo digo que el mundo cambia y los lenguajes deberían intentar hacerlo al mismo ritmo... bien es cierto que a C++ se le está dando últimamente cierto impulso con los estándares C++11 y C++14... pero al final, como siempre, la compatibilidad acaba siendo un lastre importante... es un punto controvertido, lo se, pero mi opinión es que se deberían ir dejando de lado ciertas características en vez de únicamente añadir nuevos elementos al lenguaje.

rir3760

Cita de: zShackra en 12 Noviembre 2014, 04:12 AMhe creado una función personalizada de la función getline() que aparece en el libro de K&R (en su primer capítulo)
Algunos comentarios:

* Quien llama a tu función es responsable de pasar argumentos validos, si revienta es su problema. Por ello en mi opinión deberías quitar la validación de los argumentos y si piensas dejarla debes retornar 0 (NULL es usualmente un puntero de tipo "void *").

* En caso de una linea vacía, error y fin de archivo tu función retorna cero, para discriminar los casos habrá que llamar a feof y ferror (mejor deja el avance de linea y ya tienes un indicador).

* El ultimo if + while se puede sustituir por un bucle while con la lectura del siguiente carácter como su cuerpo.

Un saludo
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

#9
Cita de: eferion en 12 Noviembre 2014, 09:03 AMLas macros que inyectan código hay que manejarlas con cuidado... en este caso, si tienes otra variable que se llama "c" dentro de la función en la que incluyas esta macro vas a tener problemas de compilación.

A mi modo de ver es mejor crear una función específica... pero no deja de ser mi opinión.

Sí, quizá sea mejor crear una función...

Cita de: daryo en 12 Noviembre 2014, 14:45 PMfunciona en linux.Solo un pequeñp cambio:
buffer[i++] = '\0';

Gracias por confirmarme lo de GNU/Linux, no veo utilidad en aumentar i para devolver la cantidad de caracteres leídos... estaríamos devolviendo el tamaño completo del arreglo, pero bien especifiqué antes que:

Cita de: zShackra en 12 Noviembre 2014, 04:12 AM
(...) simplemente se leen hasta max - 1 caracteres y se asignan a buffer[] (...)

Es decir, la función devuelve la cantidad de caracteres leídos, no toma en cuenta el carácter nulo.

Cita de: rir3760 en 12 Noviembre 2014, 16:52 PM
Algunos comentarios:

* Quien llama a tu función es responsable de pasar argumentos validos, si revienta es su problema. Por ello en mi opinión deberías quitar la validación de los argumentos y si piensas dejarla debes retornar 0 (NULL es usualmente un puntero de tipo "void *").

* En caso de una linea vacía, error y fin de archivo tu función retorna cero, para discriminar los casos habrá que llamar a feof y ferror (mejor deja el avance de linea y ya tienes un indicador).

* El ultimo if + while se puede sustituir por un bucle while con la lectura del siguiente carácter como su cuerpo.

Un saludo

Cuando dices que puede reventar, específicamente ¿a qué te refieres?. He modificado el código un poco para tratar de cubrir los errores que hasta ahora puedo corregir (voy por el capítulo 2 del libro de K&R, por eso no he usado correctamente los argumentos de las funciones ni he tocado los punteros directamente).