Semana 6¶
Esta semana terminaremos la unidad 2
Trayecto de acciones, tiempos y formas de trabajo¶
Fase 6 (RETO-continuación)¶
- Fecha: agosto 10 al 14 de 2020.
- Descripción: procede con la solución del reto.
- Recursos: lee el enunciado en la semana 4 y la RÚBRICA
- Duración de la actividad:
- 1 hora para solucionar dudas.
- 3 horas de trabajo autónomo
- Forma de trabajo: individual.
Fase 7 (sustentación):¶
- Fecha: agosto 10 al 14.
- Descripción: realiza el video de sustentación.
- Recursos: para realizar el video de sustentación te recomiendo los siguientes recursos
- Planea el video en base a la rúbrica.
- Software para capturar OBS Studio.
- Ver este tutorial para el manejo de OBS Studio.
- Duración de la actividad: 2 horas de trabajo autónomo
- Forma de trabajo: individual
Fase 8 (retroalimentación):¶
- Fecha: agosto 14 de 2020.
- Descripción: publicación de la solución del reto.
- Recursos: se publicará la solución del RETO.
- Duración de la actividad: 40 minutos faltantes de la sesión del lunes para leer la solución y escribir dudas para compartirlas en la sesión de agosto 17.
- Forma de trabajo: trabajo individual en la acción de mejora.
Una posible solución al reto¶
Realice un programa que permita crear un base de datos de estudiantes. Cada registro de la base de datos estará dado por: número de cédula, nombre y semestre. Cada registro corresponde a un estudiante.
Implemente los siguientes comandos:
mkdb nombre tamaño : crea una base de datos especificando el nombre y la cantidad de registros.
loaddb nombre : carga la base de datos en memoria desde el archivo especificado. El comando debe indicar si la base de datos se cargó correctamente o no existe. La base de datos debe cargarse en memoria dinámica antes de poder aplicar los siguientes comandos.
savedb nombre : este comando salva la base de datos en el archivo especificado.
readall : lee todos los registros de la base de datos.
readsize : lee la cantidad de registros de la base datos.
mkreg cedula nombre semestre : crea un nuevo registro en la base de datos.
readreg cédula : busca en la base de datos por número de cédula. En caso de encontrar la cédula imprime el registro completo.
exit : salir del programa. Antes de terminar debe preguntar si se desea salvar la base de datos en el archivo especificado con el comando loaddb.
Cada comando deberá implementarse como una función.
Cada registro es así:
1 2 3 4 5 6 | struct estudiante
{
int cedula;
char nombre[30];
int semestre;
};
|
Ingresar comandos¶
La primera característica que vamos a construir en nuestro programa es la capacidad de leer los comandos y los argumentos que el usuario pasará por la línea de comandos.
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main (void){
while(1){
}
return EXIT_SUCCESS;
}
|
El programa será un ciclo infinito que procesará continuamente comandos y en condiciones normales, solo terminará si se ingresa el comando exit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define COMMANDBUFFERMAXSIZE 50
int main (void){
char commandBuffer[COMMANDBUFFERMAXSIZE];
while(1){
printf(">");
fgets(commandBuffer, COMMANDBUFFERMAXSIZE, stdin);
}
return EXIT_SUCCESS;
}
|
El programa en cada ciclo le mostrará al usuario un prompt (>) que
servirá para indicarle que ya está listo para recibir un nuevo comando.
Luego utilizamos la función fgets para leer una cadena de caracteres
del flujo de entrada. Se espera que la cadena de caracteres termine en
con un enter (\n) seguido de un carácter NULL (0) que indica
el fin de la cadena.
fgets espera que le pasemos la dirección en memoria donde guardaremos los caracteres. En este caso, será un arreglo de caracteres alojados en el stack y con una capacidad máxima de COMMANDBUFFERMAXSIZE caracteres.
fgets retorna NULL si ocurre algún problema con la lectura.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define COMMANDBUFFERMAXSIZE 50
int main (void){
char commandBuffer[COMMANDBUFFERMAXSIZE];
while(1){
printf(">");
if ( fgets(commandBuffer, COMMANDBUFFERMAXSIZE, stdin) != NULL){
}
else{
perror("Error: ");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
|
Podemos leer el valor retornado por fgets para decidir si procesamos o no la cadena de entrada. Note que en caso de error, estamos usando la función perror para imprimir un mensaje que describa el error producido por fgets.
Nota
Cuando ocurre un error fgets coloca información en la variable entera y global errno a la cual podemos acceder con perror, solo que perror además interpreta el valor almacenado en la variable e imprime un texto descriptivo.
Esta es la documentación de fgets:
NAME
fgets - get a string from a stream
SYNOPSIS
#include <stdio.h>
char *fgets(char *s, int n, FILE *stream);
DESCRIPTION
The fgets() function reads bytes from stream into the array pointed to by s, until n-1 bytes are read, or a newline character is read and transferred to s, or an end-of-file condition is encountered. The string is then terminated with a null byte. The fgets() function may mark the st_atime field of the file associated with stream for update. The st_atime field will be marked for update by the first successful execution of fgetc(), fgets(), fgetwc(), fgetws(), fread(), fscanf(), getc(), getchar(), gets() or scanf() using stream that returns data not supplied by a prior call to ungetc() or ungetwc().
RETURN VALUE
Upon successful completion, fgets() returns s. If the stream is at end-of-file, the end-of-file indicator for the stream is set and fgets() returns a null pointer. If a read error occurs, the error indicator for the stream is set, fgets() returns a null pointer and sets errno to indicate the error.
Una vez tenemos la cadena en commandBuffer podemos proceder a procesarla. Lo primero que haremos será eliminar el ENTER:
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 | #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define COMMANDBUFFERMAXSIZE 50
int main(void) {
char commandBuffer[COMMANDBUFFERMAXSIZE];
while (1) {
printf(">");
if (fgets(commandBuffer, COMMANDBUFFERMAXSIZE, stdin) != NULL) {
int commandSize = strlen(commandBuffer);
if (commandBuffer[commandSize - 1] != '\n') {
printf("Error: command too long \n");
return EXIT_FAILURE;
} else {
commandBuffer[commandSize - 1] = 0;
}
} else {
perror("Error: ");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
|
La función strlen (está prototipada en string.h) nos permite encontrar el tamaño de la cadena. Recuerde que la cadena incluye el ENTER y en caso de estar en la cadena se ubicará en la posición commandSize - 1. Note que si en esta posición no está el ENTER, quiere decir que la cadena ingresada por el usuario supera la capacidad de commandBuffer y por tanto, fgets truncará la cadena. Por ejemplo, si cambiamos la capacidad de commandBuffer de 50 a 10:
Para eliminar el ENTER de la cadena basta con reemplazarlo con un 0, indicando así que la cadena termina en este punto.
Implementación del comando mkdb nombre tamaño¶
Las siguientes líneas obtienen los argumentos de mkdb y llaman la función que realizará la acción.
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int makeDatabasefn(int);
#define COMMANDBUFFERMAXSIZE 50
const char makeDatabase[] = "mkdb";
char currentDataBaseName[COMMANDBUFFERMAXSIZE];
int currentDataBaseSize = 0;
struct estudiante
{
int cedula;
char nombre[30];
int semestre;
};
struct estudiante *pcurrentDataBase;
int main(void) {
char commandBuffer[COMMANDBUFFERMAXSIZE];
while (1) {
printf(">");
if (fgets(commandBuffer, COMMANDBUFFERMAXSIZE, stdin) != NULL) {
int commandSize = strlen(commandBuffer);
if (commandBuffer[commandSize - 1] != '\n') {
printf("Error: command too long \n");
return EXIT_FAILURE;
} else {
commandBuffer[commandSize - 1] = 0;
}
if (0 == strncmp(makeDatabase, commandBuffer, strlen(makeDatabase)) ) {
int result = sscanf(commandBuffer, "%*s %s %d", currentDataBaseName, ¤tDataBaseSize);
if (result != 2) {
currentDataBaseName[0] = 0;
currentDataBaseSize = 0;
printf("Enter valid arguments\n");
}
else {
if (makeDatabasefn(currentDataBaseSize) != 1) {
printf("Database can't be created\n");
}
}
}
} else {
perror("Error: ");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int makeDatabasefn(int size){
int success = 0;
pcurrentDataBase = malloc( sizeof(struct estudiante)*size );
if(pcurrentDataBase != NULL) success = 1;
return success;
}
|
La función strncmp compara los strlen(makeDatabase) primeros bytes del arreglo de caracteres makeDatabase con commandBuffer. Si son iguales devuelve 0. sscanf lee la cadena de entrada y la procesa como scanf. Finalmente, makeDatabase crea la base datos en memoria dinámica.
La línea pcurrentDataBase = malloc( sizeof(struct estudiante)*size ); crea
tantos registros como lo indique size.
Implementación del comando loaddb nombre¶
Este comando lee la base de datos desde un archivo y la carga en memoria dinámica. Cada archivo contiene en su primera línea el tamaño de la base de datos y luego, en las demás líneas, un registro, por línea, compuesto por la cédula, nombre y semestre. Cada uno de los campos anteriores está separado por un espacio.
Primero añadimos el código que permite leer el comando y los argumentos:
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 | .
.
.
// definición del nuevo comando
const char loadDatabase[] = "loaddb";
.
.
.
// if para leer mkdb
.
.
.
else if( 0 == strncmp(loadDatabase, commandBuffer, strlen(loadDatabase) ) ){
char name[COMMANDBUFFERMAXSIZE];
int result = sscanf(commandBuffer, "%*s %s",name);
if(result != 1){
printf("Enter a data base name\n");
}
else{
if( loadDatabasefn(name) == 0){
printf("Can't load the database\n");
}
else{
strncpy(currentDataBaseName, name, COMMANDBUFFERMAXSIZE);
}
}
}
|
Y la función que leerá el archivo y cargará en memoria dinámica la base de datos:
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | //1. el prototipo
int loadDatabasefn(char *);
.
.
.
//2. Crear un contador de registros
int currentDataBaseRegister = 0;
.
.
.
//3. El código
int loadDatabasefn(char * dataBaseFileName){
int currentDataBaseSizeTmp = 0;
struct estudiante *pcurrentDataBaseTmp;
int currentDataBaseRegisterTmp = 0;
int result = 0;
FILE *fp = fopen(dataBaseFileName, "r");
if(fp == NULL){
perror("Error: ");
}
else{
int scanfStatus = fscanf(fp,"%d", ¤tDataBaseSizeTmp);
if(feof(fp) == 0){
if(scanfStatus == 1){
pcurrentDataBaseTmp = (struct estudiante *) malloc( sizeof(struct estudiante)*currentDataBaseSizeTmp );
if(pcurrentDataBaseTmp != NULL){
while(1){
int scanfStatus = fscanf(fp,"%d %s %d", &((pcurrentDataBaseTmp + currentDataBaseRegisterTmp)->cedula),
(pcurrentDataBaseTmp + currentDataBaseRegisterTmp)->nombre,
&((pcurrentDataBaseTmp + currentDataBaseRegisterTmp)->semestre) );
if(feof(fp) == 0){
if(scanfStatus != 3){
free(pcurrentDataBaseTmp);
break;
}
else{
currentDataBaseRegisterTmp++;
}
}
else{
result = 1;
pcurrentDataBase = pcurrentDataBaseTmp;
currentDataBaseSize = currentDataBaseSizeTmp;
currentDataBaseRegister = currentDataBaseRegisterTmp;
break;
}
}
}
}
else{
printf("Can't read database size\n");
}
}
else{
printf("Can't read database\n");
}
fclose(fp);
}
return result;
}
|
Note que la lectura de las líneas del archivo se hace en un ciclo infinito del cual se sale cuando una lectura al archivo retorne EOF. Tenga en cuenta que es necesario leer el archivo para poder obtener un EOF. Es por ello que primero se hace un fscanf y luego se llama feof.
Implementación de los demás comandos¶
Se incluye ahora la implementación final al problema con algunas variaciones:
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COMMANDMAXSIZE 50
#define NAMEMAXSIZE 30
char commandBuffer[COMMANDMAXSIZE + 1];
const char makeDatabase[] = "mkdb";
const char loadDatabase[] = "loaddb";
const char saveDatabase[] = "savedb";
const char readAllRegisters[] = "readall";
const char readDbSize[] = "readsize";
const char readDbCapacity[] = "readcapacity";
const char makeRegister[] = "mkreg";
const char readRegister[] = "readreg";
const char quit[] = "exit";
int makeDatabasefn(int);
int loadDatabasefn(char *);
void saveDatabasefn(char *);
void readAllRegistersfn(void);
int readsizefn(void);
int makeRegisterfn(int, char *, int );
int readRegisterfn(int);
void quitfn(void);
struct estudiante
{
int cedula;
char nombre[30];
int semestre;
};
char currentDataBaseName[50];
int currentDataBaseSize = 0;
struct estudiante *pcurrentDataBase;
int currentDataBaseRegister = 0;
int main()
{
while(1){
printf(">");
if ( fgets(commandBuffer, COMMANDMAXSIZE + 1,stdin) != NULL){
int commandSize = strlen(commandBuffer);
if(commandBuffer[commandSize - 1] != '\n'){
printf("Error: command too long \n");
return EXIT_FAILURE;
}
else{
commandBuffer[commandSize - 1] = 0;
}
if( 0 == strncmp(makeDatabase, commandBuffer, strlen(makeDatabase) ) ){
char* token = strchr(commandBuffer, ' ');
if(token != NULL){
int result = sscanf(token + 1, "%s %d", currentDataBaseName, ¤tDataBaseSize);
if(result != 2){
currentDataBaseName[0] = 0;
currentDataBaseSize = 0;
printf("Enter a valid arguments\n");
}
else{
if ( makeDatabasefn(currentDataBaseSize) != 1){
printf("Database can't be created\n");
}
}
}
else{
printf("Enter a valid arguments\n");
}
}
else if ( 0 == strncmp(readAllRegisters, commandBuffer, strlen(readAllRegisters) ) ){
readAllRegistersfn();
}
else if ( 0 == strncmp(makeRegister, commandBuffer, strlen(makeRegister) ) ){
char* token = strchr(commandBuffer, ' ');
char name[50];
int cedula;
int semestre;
if(token != NULL){
int result = sscanf(token + 1, "%d %s %d", &cedula, name,&semestre);
if(result != 3){
printf("Enter a valid register arguments\n");
}
else{
if( makeRegisterfn(cedula, name, semestre) != 1){
printf("Data base is full, register was not created\n");
}
}
}
}
else if( 0 == strncmp(saveDatabase, commandBuffer, strlen(saveDatabase) ) ){
if(currentDataBaseName[0] == 0){
printf("Load a data base first\n");
continue;
}
char* token = strchr(commandBuffer, ' ');
if(token != NULL){
int result = sscanf(token + 1, "%s", currentDataBaseName);
if(result != 1){
printf("Enter a name\n");
}
else{
saveDatabasefn(currentDataBaseName);
}
}
else{
printf("Enter a name\n");
}
}
else if( 0 == strncmp(quit, commandBuffer, strlen(quit) ) ){
if(currentDataBaseName[0] == 0){
printf("No active db\n");
continue;
}
printf("save data base with name %s? y/n: ",currentDataBaseName);
if (fgets(commandBuffer, COMMANDMAXSIZE + 1,stdin) != NULL){
int commandSize = strlen(commandBuffer);
commandBuffer[commandSize - 1] = 0;
if( 0 == strncmp("y", commandBuffer, 1) ){
saveDatabasefn(currentDataBaseName);
}
}
return EXIT_SUCCESS;
}
else if( 0 == strncmp(loadDatabase, commandBuffer, strlen(loadDatabase) ) ){
char* token = strchr(commandBuffer, ' ');
char name[50];
if(token != NULL){
int result = sscanf(token + 1, "%s",name);
if(result != 1){
printf("Enter a data base name\n");
}
else{
if( loadDatabasefn(name) == 0){
printf("Can't load de database\n");
}
else{
strncpy(currentDataBaseName, name, 50);
}
}
}
else{
printf("Enter a database name\n");
}
}
else if( 0 == strncmp(readDbSize, commandBuffer, strlen(readDbSize) ) ){
printf("%d\n",currentDataBaseRegister);
}
else if( 0 == strncmp(readDbCapacity, commandBuffer, strlen(readDbCapacity) ) ){
printf("%d\n",currentDataBaseSize);
}
else if( 0 == strncmp(readRegister, commandBuffer, strlen(readRegister) ) ){
char* token = strchr(commandBuffer, ' ');
int cedula;
if(token != NULL){
int result = sscanf(token + 1, "%d",&cedula);
if(result != 1){
printf("Enter a numeric id number\n");
}
else{
if( readRegisterfn(cedula) == 0){
printf("Can't find the id number in the database\n");
}
}
}
else{
printf("Enter a id number\n");
}
}
else{
printf("Not valid command\n");
}
}
else{
perror("Error: ");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int makeDatabasefn(int size){
int success = 0;
pcurrentDataBase = (struct estudiante *) malloc( sizeof(struct estudiante)*size );
if(pcurrentDataBase != NULL) success = 1;
return success;
}
void readAllRegistersfn(void){
for(int i = 0; i < currentDataBaseRegister; i++){
printf("registro %d cedula: %d, nombre: %s, semestre: %d\n", i+1, (pcurrentDataBase + i)->cedula,(pcurrentDataBase + i)->nombre, (pcurrentDataBase + i)->semestre);
}
}
int makeRegisterfn(int cedula, char * nombre, int semestre){
int result = 0;
if(currentDataBaseRegister < currentDataBaseSize){
(pcurrentDataBase+ currentDataBaseRegister)->cedula = cedula;
strncpy( (pcurrentDataBase+ currentDataBaseRegister) ->nombre, nombre, NAMEMAXSIZE);
(pcurrentDataBase+ currentDataBaseRegister)->semestre = semestre;
currentDataBaseRegister++;
result = 1;
}
return result;
}
void saveDatabasefn(char * name){
FILE *fp = fopen(name, "w+");
fprintf(fp,"%d\n",currentDataBaseSize);
for(int i = 0; i < currentDataBaseRegister; i++){
fprintf(fp, "%d %s %d\n", (pcurrentDataBase + i)->cedula,(pcurrentDataBase + i)->nombre, (pcurrentDataBase + i)->semestre);
}
fclose(fp);
}
int loadDatabasefn(char * dataBaseFileName){
int currentDataBaseSizeTmp = 0;
struct estudiante *pcurrentDataBaseTmp;
int currentDataBaseRegisterTmp = 0;
int result = 0;
FILE *fp = fopen(dataBaseFileName, "r");
if(fp == NULL){
perror("Error: ");
}
else{
int scanfStatus = fscanf(fp,"%d", ¤tDataBaseSizeTmp);
if(feof(fp) == 0){
if(scanfStatus == 1){
pcurrentDataBaseTmp = (struct estudiante *) malloc( sizeof(struct estudiante)*currentDataBaseSizeTmp );
if(pcurrentDataBaseTmp != NULL){
while(1){
int scanfStatus = fscanf(fp,"%d %s %d", &((pcurrentDataBaseTmp + currentDataBaseRegisterTmp)->cedula), (pcurrentDataBaseTmp + currentDataBaseRegisterTmp)->nombre, &((pcurrentDataBaseTmp + currentDataBaseRegisterTmp)->semestre) );
if(feof(fp) == 0){
if(scanfStatus != 3){
free(pcurrentDataBaseTmp);
break;
}
else{
currentDataBaseRegisterTmp++;
}
}
else{
result = 1;
pcurrentDataBase = pcurrentDataBaseTmp;
currentDataBaseSize = currentDataBaseSizeTmp;
currentDataBaseRegister = currentDataBaseRegisterTmp;
break;
}
}
}
}
else{
printf("Can't read database size\n");
}
}
else{
printf("Can't read database\n");
}
fclose(fp);
}
return result;
}
int readRegisterfn(int id){
int result = 0;
for(int i = 0; i < currentDataBaseRegister;i++){
if( (pcurrentDataBase + i)->cedula == id ){
result = 1;
printf("registro %d cedula: %d, nombre: %s, semestre: %d\n", i+1, (pcurrentDataBase + i)->cedula,(pcurrentDataBase + i)->nombre, (pcurrentDataBase + i)->semestre);
break;
}
}
return result;
}
|
Introducción al concepto de proceso¶
Antes de estudiar teóricamente que es un proceso vamos a analizar cómo los podemos crear:
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 | #include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t pid_hijo;
printf("Este proceso va a crear otro proceso\n\n");
printf("El pid del programa padre es: %d\n",(int)getpid());
pid_hijo = fork();
switch(pid_hijo) {
case -1: /* Error */
printf("Error al crear el proceso");
return -1;
case 0: /* Código ejecutado por el hijo */
printf("PROCESO HIJO:\n");
printf("Soy el hijo, mi PID es %d\n",(int)getpid());
break;
default: /* Código ejecutado por el padre */
printf("PROCESO PADRE: Proceso hijo con PID %d creado\n",(int)pid_hijo);
}
/* Esta línea será ejecutada por ambos procesos, pero en
en diferente contexto (el de cada proceso) */
printf("Soy el proceso %d terminando \n",(int)getpid());
exit(EXIT_SUCCESS);
}
|