#210 lo estoy sopesando, pero quiza pase xD
#211 Hombre creo que es lo interesante de C y cuando se comienza a poner chungo, listas simples, listas bidireccionales, etc... nosotros ahora estamos con los arboles AVL.
bastante le cuesta a la gente que esta aprendiendo esto, hacer ejercicios sencillos.. si te pones a explicar listas enlazadas y for sobre punteros, habrá mindblows en masa
las estructuras complejas deberíais dejarla para el curso completo de programacion en lenguaje C - Nivel Avanzado.
11 - Manejo de ficheros
Todos los programas que hemos hecho hasta ahora compartían una característica, y es que eran volátiles. Todos los datos almacenados en variables se perdían una vez cerrado el programa, y de ese modo resultaba imposible continuar el trabajo en diferentes sesiones de ejecución.
En esta lección aprenderemos a aprovechar el disco duro para guardar el estado de nuestras variables, y posteriormente leer esos estados. También del mismo modo podremos leer archivos externos y procesarlos a nuestro gusto.
el tipo FILE*
Todo este tema va a girar en torno a un nuevo tipo de variable, llamado FILE (si, en mayúsculas), y más concretamente en punteros a variables FILE.
FILE* pf;
Estas variables FILE* apuntarán a un solo fichero ya existente, o bien creado por el programa, y será la variable que usaremos en el programa para enviar las órdenes de leer o escribir, abrir y cerrar fichero.
fopen - archivos binarios y de texto
Una vez declarada la variable FILE*, hay que rellenarla con la auténtica dirección de memoria a un fichero. La instrucción que se encarga de esto está en stdio.h y es fopen.
pf = fopen("ejemplo.txt", "wt");
La función fopen devuelve a su izquierda el puntero que buscamos, por lo que debemos asignarlo a nuestra variable anteriormente creada, igual que en el ejemplo, y recibe dos datos de entrada. Por un lado el nombre del fichero real en forma de un array de caracteres (por eso las dobles comillas), y por otro, tambien en cadena de texto, el modo en el que se va a procesar el fichero, las posibilidades son, por un lado el modo:
-
r lectura
-
w escritura
Y por otro lado el tipo:
-
t texto
-
b binario
Por tanto el tipo "wt" es "un fichero de texto en modo escritura", rb "fichero binario en modo lectura", etc. Existen más modos de apertura de un fichero, como "a" append, que abre el fichero desde el final, o los modos de lectura/escritura simultanea. Pero por el momento basta con w y r. Un fichero abierto en modo lectura con "r" debe existir para poder acceder a el, y para "w", si el archivo no existe, será creado vacío.
Sobre los tipos de fichero, la diferencia entre texto y binario, es que un fichero de tipo texto agrupa sus datos en octetos, grupos de 8 bits, dotando a cada octeto un valor de caracter, coincidiendo con el valor en la tabla ascii. Un fichero de texto que se rellene mediante un programa puede ser interpretado y explorado de manera perfectamente inteligible para un ser humano mediante cualquier editor de texto. Si los datos que deseamos guardar en un fichero son caracteres, o cadenas de caracteres, un fichero de texto es el indicado.
Por otro lado un archivo binario puede (o no), agrupar sus datos en octetos, y puede guardar cualquier cosa, desde números enteros, hasta estructuras complejas. Al intentar abrir un archivo binario con un editor de texto, el editor intenta agrupar los datos de 8 en 8 y darles un ímbolo a cada uno, pero en este caso el resultado será incomprensible. Para la visualización de archivos binarios recomiendo un programa llamado HxD, que podeis encontrar aquí.
El cualquiera de los casos en los que abramos un fichero con fopen, tendremos que cerrar el fichero cuando ya no lo necesitemos, para ello la función usada es fclose.
fclose(pf);
archivos de texto
Como se ha dicho, un archivo de texto puede ser editado y visualizado desde cualquier editor de texto, pero el manejar este tipo de ficheros desde C puede ser útil para realizar algún procesado complejo, o bien extraer unos datos determinados de manera automatizada, entre otras cosas.
Las instrucciones para leer y escribir ficheros de texto son ya conocidas por todo el mundo, ya que no son otras que fprintf y fscanf.
int main(void){
FILE* pf;
char frase[30];
pf = fopen("ejemplo.txt", "wt");
strcpy(frase, "hello world");
fprintf(pf, frase);
fclose(pf);
return 0;
}
En este sencillo ejemplo se abre un fichero de texto en modo escritura y se usa fprintf para escribir dentro una cadena de texto. En este caso no queremos imprimir al fichero estandar stdout, sino a nuestro fichero, pf. Tras ejecutarlo, un fichero nuevo llamado ejemplo.txt aparecerá en el mismo directorio en el que teníamos el .exe. Si se desea colocarlo en otro directorio, puede especificarse la url completa en el fopen.
int main(void){
FILE* pf;
char letra;
pf = fopen("ejemplo.txt", "rt");
while(!feof(pf)){
fscanf(pf, "%c", &letra);
fprintf(stdout, "%c", letra);
}
fclose(pf);
fprintf(stdout, "\n");
system("pause");
return 0;
}
En esta ocasión el programa usa fscanf para leer caracter a caracter el contenido del fichero que ya habiamos creado antes, ejemplo.txt, para posteriormente mostrar uno a uno esos caracteres por pantalla. El archivo ha sido esta vez abierto en modo lectura "rt" y se usa una función desconocida, feof, que detecta el "end of file". Cada vez que se hace una petición de lectura al fichero, un cursor se mueve a la siguiente posición, para que cuando volvamos a solicitar un caracter, nos de cada vez el siguiente. Cuando el cursor ya se ha movido hasta el fin del fichero, feof devolverá true,. indicando que se ha llegado al final del archivo. por tanto el bucle while (!feof(pf)) indica que se va a realizar la acción del bucle hasta que se termine el fichero.
archivos binarios
Cualquier tipo de dato puede leerse y escribirse en un archivo binario, desde numero, hasta texto y estructuras complejas, pagando el precio de que un archivo binario es a simple vista incomprensible por un ser humano. fprintf y fscanf pueden dar resultados inesperados al usar archivos binarios, por lo que es necesario usar otras dos funciones de lectura y escritura, fread y fwrite.
int main(void){
FILE* pf;
int n1 = 128;
float n2 = 27.421;
pf = fopen("datos.dat", "wb");
fwrite (&n1 , sizeof(int), 1, pf);
fwrite (&n2 , sizeof(float), 1, pf);
fclose(pf);
return 0;
}
De nuevo empezamos con un bloque de escritura. Una vez creadas dos variables de tipos distintos y abierto un fichero en modo escritura al que llamaremos "datos.dat", usamos fwrite para introducir nuestras variables al archivo. :a función fwrite recibe 1) un puntero a la variable que queremos meter, 2) el tamaño en bits de esa variable, que si no sabeis, podeis averiguar con el operador sizeof(), 3) el número de esas variables a meter (habitualmente 1), y 4) el puntero al fichero donde queremos meterlo.
Una vez ejecutado este programa aparecerá el fichero "datos.dat" en nuestro directorio de trabajo. Abrirlo con un editor de texto no nos permitirá ver su contenido, pero mediante el HxD podemos ver esto:
Pensandolo un poco se puede ver lo que ha sucedido. El valor 80 00 00 00 en hexadecimal coincide con el entero 128, mientras que los otros datos corersponden al número en coma flotante.
int main(void){
FILE* pf;
int m1;
float m2;
pf = fopen("datos.dat", "rb");
fread (&m1, 1, sizeof(int), pf);
fread (&m2, 1, sizeof(float), pf);
fclose(pf);
return 0;
}
Y en este caso este es el bloque de lectura a partir de un fichero ya dado. El cursor que se comentó en los ficheros de tipo texto sigue existiendo y puede usarse feof libremente, por lo que los datos hay que leerlos en el mismo orden en el que se introducieron. Es decir, en este caso primero fue un entero y luego un float, por lo que hay que leerlos en ese mismo orden. La función fread funciona de una manera muy parecida a fwrite, solo que con los parámetros descolocados, esta vez siendo la variable a rellenar, el número de datos, el tamaño de esos datos en bits y el fichero de lectura.
deberes- lección 11
1 - Crear con un editor de texto un archivo con un párrafo de texto o cualquier cosa inteligible, a continuación crear un programa que vaya leyendo ese fichero, y cree otro fichero cuyo contenido sea el mismo que el primero, pero con una palabra por cada línea.
2 - Crear un archivo binario que contenga 64 valores de 8 bits que sean la tabla de onda de un seno. Es decir esos valores de 8 bits deben de ser las muestras de una función seno cuyo valor oscile entre 0 y 255. Para que el resultado sea correcto el archivo creado debería ocupar 64 bytes, ni un bit mas ni uno menos. Para usar variables de 8bits podeis usar el tipo uint8_t, que se encuentra definido dentro de la librería stdint.h
3 - adaptar el programa de gestión de coches para que guarde el estado actual de las variables necesarias y sea posible continuar sesiones anteriores. Para ello incluir dos nuevas opciones: guardar el estado actual a una variable y cargar el estado desde fichero dado:
Tengo que recuperar la UF3 de C que es sobre manejo de ficheros, asi que +1.
Nunca esta de mas otro punto de vista mas alla del pdf del profesor.
Me he apuntado a una movida de mi uni donde me enseñaran a programar algoritmos para los concursos esos.. a ver si con el tiempo soy "bueno" y me sirve de algo
12 - Modularidad: tipos abstractos de datos
Hasta ahora las instrucciones y funciones que habíamos usado eran propias del lenguaje C, estaban contenidas en una librería propia o externa, o bien las teniamos creadas junto a nuestro programa en el mismo archivo.
A medida que pasa el tiempo los programas que se hacen van complicándose y es imperativo poner algo de orden en lo que hacemos. Se puede empezar entonces a clasificar los códigos que hacemos en distintos archivos, para luego trabajar más a fondo en el fichero donde tengamos nuestra función main, sin tener que preocuparnos por el resto. Para ello habrá que crear nosotros mismos la librería, el archivo .h e incluirlo en nuestro programa.
creacion de librerias propias
Una librería está formada por dos ficheros fuente, que serán compilados y enlazados a nuestro programa principal automáticamente si se hacen las cosas bien. Primeramente debemos crear un archivo .h (header), que contendrá los siguientes elementos:
-
Definición
-
Constantes y macros
-
Estructuras y nuevos tipos
-
prototipos de las funciones
Puesto que el archivo .h suele quedar muy corto con tan solo estas cosas, se suele aprovechar para documentar de manera completa lo que hacen todas las funciones, y todo lo que se nos ocurra que pueda necesitar datos adicionales para la persona que analice el código.
La definición es un paso fundamental totalmente necesario en el .h. Esta definición consiste en una instrucción #include {nombre} que se debe hacer una sola vez en toda la ejecución para evitar un bucle infinito de includes. Existe un mecanismo que se usa siempre para asegurarse de que solo se pasará por aquí una vez, y es usando un condicional if, pero no un if al uso, sino otra premisa de precompilado llamada #ifndef.
Supongamos un archivo algoritmos.h, el comienzo del archivo sería así:
#ifndef _ALGORITMOS_H_
#define _ALGORITMOS_H_
// Aquí escribiremos toda nuestra librería.
#endif
Puesto que el designador del define tiene que ser único, se suele escribir con guiones bajos al principio y al final de su nombre. Si el archivo tiene nombre español no debería haber ningun problema, pero puesto que no es muy buena costumbre "programar en español" es recomendable poner los guiones bajos siempre.
El siguiente paso es incluir las constantes que usará la librería, de la manera habitual
#define PI 3.141592
#define E 2.717281
#define MAX_ARRAY 1024
//etc
Si la librería hace uso de estructuras o tipos creados artificialmente, se pondrán a continuación
typedef char dni[10];
typedef char email[100];
typedef struct{
int numero;
char calle[MAX_ARRAY];
char ciudad[MAX_ARRAY];
int habitantes;
} casa;
Y por ultimo los prototipos de las funciones. Sólo prototipos, las funciones nos e codificarán aquí.
int* sumarArray(int* sum1, int* sum2);
int* multArray(int* op1, int* op2);
int sumatorio(int* vector);
Una vez terminado el .h, es turno de codificar nuestras funciones en otro archivo .c que se llame igual que el .h, y que incluya esta cabecera al principio de sus lineas.
Archivo algoritmos.c:
#include "algoritmos.h"
int* sumarArray(int* sum1, int* sum2){
//blablabla
}
int* multArray(int* op1, int* op2){
//blablabla
}
int sumatorio(int* vector){
//blablabla
}
Habreis notado que el include esta con comillas dobles "" y no con mayor que-menor que <> Por qué? Rodear los archivos .h con <> significa que la librería es nativa, y está en los archivos de programa del compilador. Si explorais por los archivos del Dev C++, encontrareis una lista bastante grande de archivos .h en los que se encuentran los ya conocidos stdio, stdlib, math, y muchos más. Sin embargo cuando las librerías no son nativas (como es el caso), se usa "", y eso le sirve al compilador para saber que debe buscar la librería en el mismo directorio que el programa principal.
Una vez creado el .c, solo queda añadir la librería a nuestro programa principal, donde tenemos situado la función main, de nuevo, por supuesto, con comillas.
TAD reciclaje de código y abstracción
TAD significa Tipo abstracto de datos, y es la filosifía que debe seguirse al crear nuevas librerías, es decir, nuestra librería tiene que ser abstracta, portable y reciclable, por lo que puede servirnos tanto para este proyecto que estamos ahora mismo desarrollando como para el que realicemos a continuación, sin cambiar ni una linea de código.
Para conseguir esto con éxito se deben mantener nuestras librerías lo mas sencillas y modulables posibles.
Deberes - lección 12
1 - Crear una librería completa (.h y .c) llamada complejo.h, que servirá para el manejo de números complejos en formato double. La librería debe contener:
-
Una estructura "complejo", formada por dos doubles, que son la parte real e imaginaria
-
las siguientes funciones:
complejo setReal(double d);
complejo setImg(double d);
double getReal(complejo c);
double getImg(complejo c)
complejo sumarC(complejo c1, complejo c2);
double modulo(complejo c);
double fase(complejo c);
No seais cafres y usad la librería math para las operaciones.
2 - Crear una librería llamada polinomio.h, que contendrá instrucciones para operar con polinomios en forma de arrays. Por ejemplo el polinomio 3x5 + 2x3 + 7x2 + 3x + 4 se contendría en un array de esta forma:
[4 3 7 2 0 3 ...]
Es decir, la posición [0] coincide con el valor en x0, la posición 1 corresponde con x1, y así en adelante.
Las funciones a incluir serán:
polinomio sumarP(polinomio p1, polinomio p2);
polinomio restar(polinomio p1, polinomio p2);
polinomio multiplicar(polinomio p1, polinomio p2);
#218 en el 5.3 no hacia falta que tuvieses en cuanta que algunos caracteres no son "imprimibles", trata de imprimir la lista completa desde 0 hasta 255 y veras lo que pasa
el 5.4 esta perfecto, quedate con esa estructura porque es muy utilizada
5.7 un poco enrevesado, pero funcional. Se puede hacer con menos variables y de una forma mucho más sencilla
Lo mismo para 5.8, te complicas en exceso xD
5.12 Reportadito! xDDD
Buenas voy a intentar seguir este cursillo. Acabo de empezar ahora mismo.
Aquí dejo el código de los primeros deberes, por si he cometido algún error o algo, aunque funciona perfectamente XD.
Un saludo y gracias por la labor, intentaré completarlo poquito a poco. Gracias.
#222 Guay.
Recomendación, separa por bloques, me explico:
fprintf(stdout, "Intruduce el primer valor: ");
fflush(stdin);
fscanf(stdin, "%f", &valor1);
fprintf(stdout, "Introduce el segundo valor: ");
fflush(stdin);
fscanf(stdin, "%f", &valor2);
resultado = valor1 + valor2;
fprintf(stdout, "\nLa suma de ambos valores es: %f", resultado);
resultado = valor1 - valor2;
fprintf(stdout, "\nLa resta de ambos valores es: %f", resultado);
resultado = valor1*valor2;
fprintf(stdout, "\nEl producto de ambos valores es: %f", resultado);
resultado = valor1/valor2;
fprintf(stdout, "\nLa divisi\242n de ambos valores es: %f", resultado);
Y al finalizar, que me corrija gonya si no, debes de poner
return 0;
Un saludete.
#222 Como te dice R1kz, te falta un return 0 justo al final. A estas alturas todavía no sabes por qué se pone, pero lo desvelo más adelante. El separarlo por bloques es opcional, pero intenta organizar tu código para que sea fácil de leer, tanto para tí como para el resto de personas
Animo con el curso y dale duro!
#223 haz los deberes (╯°□°)╯︵ ┻━┻
Muchas gracias a ambos, ahora mismo lo corrijo. A ver si tengo un hueco y voy a por la segunda lección .
¡Saludos!
Fantástico! Me faltan manos para darte el currazo, es una pena que no vayas a hacer uno con estructuras de datos, sería la bomba joder
#227
¿A qué te refieres con estructuras de datos? Linked lists, árboles y cosas por el estilo?
#228 Si, estruturas lineales simples y dobles (pilas, colas, listas) y arboreas (arboles binarios, n-anarios....)
Sería ya un curso serio serio.
#229 A lo mejor cuando acabe este curso, hace uno de algoritmos y estructuras de datos si lo animáis.
No se yo... xD
Lo de explicar listas dinámicas lo estuve sopesando pero lo veo muy pesado y complejo para alguien que en principio ha seguido este curso por hobby, y no por ninguna obligación. Si alguien necesita alguna explicación sobre el tema puedo ofrecerle ayuda, pero no sabría estructurar la enseñanza de datos dinámicos de manera que quedase claro a la primera y no crease ninguna controversia con lo que digo.
Sobre lo que haré cuando termine este curso... HE pensado algunas cosas, y de momento me gustaría separarme lo máximo posible de temas informáticos, de los que no soy ningún experto.
Yo podría escribir algo sobre estos temas imagino, normalmente estas cosas en plan arbol las pico usando arrays y el truco de 2n y 2n + 1 para los hijos y uso la STL de C++ o la librería de Java para cosas en plan stacks, queues, minmaxheaps y tal, pero la mayoría de estas cosas las he picado unas cuantas veces.
Yo creo que para enseñar esto en C primero habría que dobletriple asegurarse de que se entienden los punteros y luego ya meterse de lleno con estas historias. Los ejemplos y problemas que resuelven son útiles también para no tener la impresión de que estás aprendiendo inventos estrambóticos sin razón alguna.
edit: Y también asegurarse de que se entiende el principio de la recursividad y las reglas recursivas para definir estas estructuras de datos, suena pedante, pero lo veo fundamental también si quieres entender lo que estás haciendo.
#232 Creo que eso se puede enseñar mejor en Java para evitar la complejidad de lenguaje y centrarse en las estructuras en sí.
Yo estoy dando estructuras de datos. Es más, trabajamos sobre una clase Complejo y sobre ella creamos una clase vector,Abb,avl,etc. Si alguien le interesa más sobre esto quizás pueda ayudar ya que lo tengo fresco, bueno ahora peleando con las rotaciones d las inserciones en AVL.
Saludos
Aqui van los 3 primeros ejercicios del tema 9.
#1 Para el ejercicio 4 necesito un cable, no se plantearlo habia pensado en dividir la cadena larga por espacios y despues elegir las cadenas que contengan @.
#239 Primero las dudas del ejercicio 1:
Sobre lo del ejercicio 4... hazte una cadena de caracteres estilo
char test[200] = "fasdf sd fsd af asdf errejk gerg ejr smd fsadf ,as dasd [email protected] asdjajsndasnd ds,d s,d a ";
Y ahora haz una funcion que reciba esa char* test y de ahi extraiga e imprima por pantalla la dirección de correo electronico que hay ahi metida. Una direccion electronico debe ser estilo:
[grupo alfanumerico]@[grupo alfanumerico].[grupo alfanumerico]