Hola, gente.
Estoy muy feliz porque llegue al capítulo archivos, muy esperado por mi porqué es lo que mas necesito aprender en c dado me dará la posibilidad de desarrollar algo que todavía no les puedo decir porque las 2 veces que lo hice en foros me criticaron mucho (mucha energía negativa). -
Como notaran en el menú el programa es un tanto ambicioso para ser el primero, pero de entrada quiero ir a fondo. -
La primer consulta es un segmentation fault que me da en la línea 78 y por supuesto hasta la 90 me podrían decir en que estoy fallando, créanme que consulté páginas, videos (incluso los 3 de Alberto) pero no hallo la solución . -
Errores/dudas/etc.
1 - Segmentation fault
2 - Grabar en archivo linealmente y no en renglones.
3 - Posicionarse en el último registro.
4 - Salto de línea anticipado en el último campo.
5 - Leer/escribir
6 - Buscar
7 - Borrar
8 - Ordenar
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int orden;
char nombre[50], direccion[50], ciudad[50], correo[50];
char telefono[20], celular[20];
char otros[200];
}Recordatorio;
void limpiar( void );
void menu( void );
void agregar( void );
int main ( int argc, char **argv ){
menu();
return 0;
}
void limpiar( void ){
system("cls||clear");
}
void menu( void ){
int opc, ok, ch;
do{
do{
limpiar( );
printf( "\n =============== Menu principal ===============\n"
"\n 1 - Primero"
"\n 2 - Ultimo"
"\n 3 - Buscar"
"\n 4 - Todos"
"\n 5 - Agregar"
"\n 6 - Modificar"
"\n 7 - Borrar"
"\n 8 - Finalizar\n"
"\n ingrese opcion.....:" );
ok = scanf( "%d", &opc ) == 1 && opc > 0 && opc <= 8;
while ((ch = getchar()) != EOF && ch != '\n');
}while( !ok );
switch ( opc ){
case 1: printf( "\n Primero" );
break;
case 2: printf( "\n Ultimo" );
break;
case 3: printf( "\n Buscar" );
break;
case 4: printf( "\n Todos" );
break;
case 5: agregar( );
break;
case 6: printf( "\n Modificar" );
break;
case 7: printf( "\n Borrar" );
break;
}
}while( opc != 8 );
}
void agregar( void ){
Recordatorio *recordatorio = NULL;
FILE *fptr = fopen ( "fichero.txt", "a" );
limpiar( );
if( fptr == NULL ){
perror(" \n\n Error en la creacion/apertura del archivo" );
}else{
printf( "\n\n Ingrese NOMBRE completo del contacto........: " );
fgets( recordatorio->nombre, 50, stdin );
printf( "\n\n Ingrese DIRECCION del contacto..............: " );
fgets( recordatorio->direccion, 50, stdin );
printf( "\n\n Ingrese CIUDAD donde reside el contacto.....: " );
fgets( recordatorio->ciudad, 50, stdin );
printf( "\n\n Ingrese CORREO del contacto.................: " );
fgets( recordatorio->correo, 50, stdin );
printf( "\n\n Ingrese TELEFONO FIJO del contacto..........: " );
fgets( recordatorio->telefono, 50, stdin );
printf( "\n\n Ingrese CELULAR del contacto................: " );
fgets( recordatorio->celular, 50, stdin );
printf( "\n\n Ingrese OTROS DATOS (200 caracteres)........: " );
fgets( recordatorio->otros, 50, stdin );
fprintf(fptr, "%s", recordatorio->nombre);
fprintf(fptr, "%s", recordatorio->direccion);
fprintf(fptr, "%s", recordatorio->ciudad);
fprintf(fptr, "%s", recordatorio->correo);
fprintf(fptr, "%s", recordatorio->telefono);
fprintf(fptr, "%s", recordatorio->celular);
fprintf(fptr, "%s", recordatorio->otros);
fclose ( fptr );
}
}
Saludos.
En esa función, recordatorio es un puntero a estructura, inicializado a NULL.
Nunca le asignas una estructura
recordatorio = (Recordatorio*)malloc(sizeof(Recordatorio));
Muchas gracias, todo correcto, te preguntaría como se podría hacer sin utilizar memoria dinámica (me parece haberlo visto) pero como voy a encerrar el código en un bucle para que se pueda ingresar los contactos que el operador desee, me viene al pelo para no tener que hacer un array de estructuras. -
Saludos.
Sin memoria dinámica, tendrías que guardar los recordatorios en un array en algún lugar, como el main, o en el ámbito global (fuera de funciones/estructuras).
Depende de lo que quieras hacer.
Una buena idea para depurar el código y encontrar el error seria imprimir las direcciones y/o valores de las variables por ejemplo en tu caso:
fprintf("%p\n",recordatorio);
Si ya sabemos que el error esta en X linea ponemos el printf justo antes del error
Eso os imprime la dirección a la cual esta apuntado, y si vemos que son puros 000000 sabremos que no lo hemos inicializado.
Saludos!
Cita de: AlbertoBSD en 26 Agosto 2016, 16:15 PM
Una buena idea para depurar el código y encontrar el error seria imprimir las direcciones y/o valores de las variables por ejemplo en tu caso:
fprintf("%p\n",recordatorio);
Si ya sabemos que el error esta en X linea ponemos el printf justo antes del error
Eso os imprime la dirección a la cual esta apuntado, y si vemos que son puros 000000 sabremos que no lo hemos inicializado.
Saludos!
Ten en cuenta que para eso hay que saber que el error es un problemacon la memoria. Para cuando supones eso, ya no necesitas debuguear así xD
Y es más, es posible que simplemente no esté inicializada, lo que podría resultar en basura diferente de 0.
Tienes toda la razon mi estimado :) ;-)
Hola y muchas gracias a los dos.
Segunda pregunta, en la imagen se muestra como el programa guarda en un archivo (en primer lugar), pero lo que quiero es que se grabe en forma lineal, como yo lo puse a mano. -
2 - Grabar en archivo linealmente y no en renglones.
(http://i68.tinypic.com/fum711.png)
Saludos.
El primer paso seria sanitizar la entrada (Limpiarla) y quitarle el \n del fin de linea para que no de el enter.
El segundo paso si lo quieres asi en forma Lineal es determinar como saber cuando empieza y termina cada registro. Se puede hacer de 2 formas,
Forma variable (Seperando con ',' o ';' o '.' o cual quier otro valor)
Forma estatica (Guardar una cantidad determinada de bytes en el archivo, rellenando los bytes restantes con valore NULL '\0')
Saludos!
¿Seria algo como esto?
fgets( recordatorio->nombre, 50, stdin );
luego contar la cantidad de caracteres +1 y remplazar el '\0' por '|', por ej.
Creo que sera evidente que no soy experto en C/C++, pero no seria valido reemplazar esto:
...
fprintf(fptr, "%s", recordatorio->nombre);
fprintf(fptr, "%s", recordatorio->direccion);
fprintf(fptr, "%s", recordatorio->ciudad);
fprintf(fptr, "%s", recordatorio->correo);
fprintf(fptr, "%s", recordatorio->telefono);
fprintf(fptr, "%s", recordatorio->celular);
fprintf(fptr, "%s", recordatorio->otros);
...
por algo como esto?
...
fprintf(fptr, "%s,%s,%s,%s,%s,%s,%s", (recordatorio->nombre, recordatorio->direccion, recordatorio->ciudad, recordatorio->correo, recordatorio->telefono, recordatorio->celular, recordatorio->otros));
...
Desconozco si fprintf se puede usar de esa forma, pero imagino que debe existir algo como para hacer eso y separar los valores usando comas (como en el ejemplo) o usando ";" o el separador que sea...
Saludos!
Hola, coterráneo.
Parece que igual toma el salto de linea '\n', me da el mismo resultado, tu código tiene 2 paréntesis de más. -
Saludos.
Ya lo solucione, dejo el código por si a alguien le pasa algo parecido. -
void reemplazar( Recordatorio *recordatorio ){
char *p=NULL;
if((p=strchr(recordatorio->nombre, '\n'))){
*p='|';
}
if((p=strchr(recordatorio->direccion, '\n'))){
*p='|';
}
if((p=strchr(recordatorio->ciudad, '\n'))){
*p='|';
}
if((p=strchr(recordatorio->correo, '\n'))){
*p='|';
}
if((p=strchr(recordatorio->telefono, '\n'))){
*p='|';
}
if((p=strchr(recordatorio->celular, '\n'))){
*p='|';
}
if((p=strchr(recordatorio->otros, '\n'))){
*p='|';
}
}
Saludos.
3 - Posicionarse en el último registro. Si tengo lo siguiente en el archivo. -
Citar1 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
2 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
3 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
4 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
5 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
6 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
7 - xxxxxxxx|xxxxxxxxxxxx|xxxxx
Lo que quiero es recuperar el número 7 para agregarle 1 y así tener un orden de ingreso, me podrían dar alguna idea de como intentarlo. -
Saludos.
Cuando se tiene registros de longitud dinamica, y no tienes los datos cargados en memoria tienes que leerlos todos y reescribir todo el archivo o solo desde la parte que cambio.
Segun se te facilite si lo reescribe todo solo haces un ciclo y escribes desde 0 todo.
Si solo quieres modificar la parte modificada, tienes que situarte donde finaliza el ultimo registro no afectado fseek() y empezar a escribir apartir de ahi
Saludos
Hola.
Al fin parece que lo conseguí. -
Una consultita, hay otra manera para hacer lo mismo sin utilizar '\n'. -
FILE *fptr = fopen( "fichero.txt", "r+" );
orden = cantCampos( fptr, orden );
int cantCampos( FILE *fptr, int orden ){
char caracter;
while ( !feof ( fptr ) ){
caracter = getc( fptr );
if( caracter == '\n' )
orden++;
}
return orden;
}
Saludos.
Hola, gente. -
Tengo un inconveniente con el siguiente código, como se muestra en la captura hace como un enter en el último campo antes de tiempo, hice unos cambios para solucionarlo y nada, los primeros 7 registros los alinee a mano. -
4 - Salto de línea anticipado en el último campo.
(http://i65.tinypic.com/14ne9uf.png)
#include <stdio.h>
#include <stdlib.h>
void limpiar( void );
int main( void ){
int n = 1, ch, orden=-1;
char nombre[80], caracter;
double salario;
FILE *pArchivo = fopen( "pruebaE.txt", "a+" );
if( pArchivo != NULL ){
while ( !feof ( pArchivo ) ){
caracter = getc( pArchivo );
if( caracter == '\n' )
orden++;
}
do{
limpiar();
orden++;
printf( "\n\n El proximo empleado es el numero..: %d", orden );
printf( "\n\n Introduzca 0 para finalizar mayor para continuar.....:" );
scanf( "%d", &n );
while ((ch = getchar()) != EOF && ch != '\n');
if( n > 0 ){
printf( "\n Introduzca el NOMBRE del empleado.....:" );
fgets( nombre, 80, stdin );
printf( "\n Introduzca el SALARIO del empleado....:" );
scanf( "%lf", &salario );
while ((ch = getchar()) != EOF && ch != '\n');
fprintf( pArchivo, "%d\t%s\t%lf \n", orden, nombre, salario );
}
}while( n > 0 );
fclose( pArchivo );
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", "pruebaE" );
}
return 0;
}
void limpiar( void ){
system("cls||clear");
}
Desde ya muchas gracias, saludos.
Es raro que te de ese error si es el mismo codigo que se uso no deberia de agregar ese enter de mas...
Sanitiza la entrada del nombre quitandole el \n o \r que quede atorado ahi:
char *busqueda = NULL;
fgets( nombre, 80, stdin );
busqueda = strstr(nombre,"\n");
*busqueda = '\0';
Hola, Alberto, buen día. -
No es el mismo código y cometí 2 veces el mismo error , fgets en casi todos los casos al final de la cadena pone el salto de línea y eso hacia que el último campo se escribiera mal en el archivo. -
Tengo otra consulta para hacerles, lo que ocurre es que tengo el programa para escribir y leer el archivo en distintas carpetas ¿alguien sabe cómo abrir archivo en Linux que estén en una carpeta distinta a la que se encuentraa el programa? -
Saludos.
Hay varias formas para hacer esto.
Hay que usar la ruta absoluta o la ruta relativa.
Esto es con ruta absoluta:
archivo = fopen("/usr/home/usuario/directorioX/archivo.txt","r");
O con ruta relativa (relativa al directorio actual de trabajo), suponiendo que estemos ya en el directorio usuario y asumiendo que el directorioX exita:
archivo = fopen("./directorioX/archivo.txt","r");
Saludos!
Estoy probando un montón de maneras y no me funciona, te dejo algunos datos para ver si me podes dar la solución. -
Esto es lo que sale al abrir un terminal
Citardaniel@daniel-H81M-S1 ~ $
Y esta es la ruta(carpetas) completa...
.../Escritorio/Prueba/Archivos Daniel/Escribir/pruebaE.txtY donde esta el programa con el que estoy trabajando es:
.../Escritorio/Prueba/Archivos Daniel/LeerSaludos.
Bueno ya lo logre con el comando locate.
FILE *pArchivo = fopen( "/home/daniel/Escritorio/Prueba/Archivos Daniel/Escribir/pruebaE.txt", "r" ); //Comando locate
pero vaya sorpresa me imprime solo el primer registro (todos los campos) pero no salta al segundo, como que se quedaría en un bucle infinito, practico un poco y si no lo logro les consulto. -
Buen día. -
Usa la ruta completa: "/home/daniel/Escritorio/Prueba/Archivos Daniel/Escribir/pruebaE.txt"
O la ruta relativa: "../Escribir/pruebaE.txt"
La ruta completa empieza en el directorio raíz /, después pasas al directorio que guarda los archivos de usuarios home, el nombre de usuario del sistema daniel y a partir de allí moverte por árbol de directorios hasta el archivo.
Las rutas relativas parten desde donde se encuentra el programa y para subir al directorio padre debes usar los dos puntos ..; una vez llegues al directorio común escribe el resto de la ruta a donde quieras llegar. Nota: para ir retrocediendo en el árbol, usa ../../ tantas veces como sea necesario.
5 - Leer/escribir
Hola.
Hoy estoy un poco cargoso, espero sepan comprender mi ansiedad por resolver esto. -
Les dejo los 2 códigos para escribir en archivos y leer desde un archivo, quisieran que lo revisen con tiempo y me digan que estoy asiendo mal, en realidad escribir me parece que funciona bien, en cuanto a leer lee solamente la primer linea y se produce un bucle infinito. -
Una cosita mas, la línea 39 del archivo leer no me place para nada, ¿con que otra función la puedo reemplazar?. -
MAFUS, gracias por el aporte y que agrado da leerte nuevamente. -
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void limpiar( void );
void leer( FILE *pArchivo );
int main( void ){
// FILE *pArchivo = fopen( "/home/daniel/Escritorio/Prueba/Archivos Daniel/Escribir/pruebaE.txt", "r" ); //Comando locate
FILE *pArchivo = fopen( "../Escribir/pruebaE.txt", "r" );
if( pArchivo != NULL ){
leer( pArchivo );
fclose( pArchivo );
}else{
printf( "\n Error al abrir %s para lectura. Finaliza el programa.", "pruebaE.txt" );
}
return 0;
}
void limpiar( void ){
system("cls||clear");
}
void leer( FILE *pArchivo ){
int orden=0;
char nombre[30];
double salario;
printf( "\n\n Orden Nombre Salario\n\n" );
while( !feof(pArchivo) ){
fscanf(pArchivo,"%d\t%[^\n]\t%lf",&orden,nombre,&salario);
printf(" %d\t%-10s\t%lf",orden,nombre,salario);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CAR 30
void limpiar( void );
void agregar( FILE *pArchivo );
int main( void ){
FILE *pArchivo = fopen( "pruebaE.txt", "a+" );
if( pArchivo != NULL ){
agregar( pArchivo );
fclose( pArchivo );
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", "pruebaE.txt" );
}
return 0;
}
void limpiar( void ){
system("cls||clear");
}
void agregar( FILE *pArchivo ){
int n = 1, ch, orden=0, escrito;
char nombre[MAX_CAR], caracter, *p=NULL;
double salario;
while ( !feof ( pArchivo ) ){
caracter = getc( pArchivo );
if( caracter == '\n' )
orden++;
}
do{
limpiar();
orden++;
printf( "\n\n El proximo empleado es el numero..: %d", orden );
printf( "\n\n Introduzca 0 para finalizar mayor para continuar.....:" );
scanf( "%d", &n );
while ((ch = getchar()) != EOF && ch != '\n');
if( n > 0 ){
printf( "\n Introduzca el NOMBRE del empleado.....:" );
fgets( nombre, MAX_CAR, stdin );
if(( p=strchr(nombre, '\n' ))){
*p='\0';
}
printf( "\n Introduzca el SALARIO del empleado....:" );
scanf( "%lf", &salario );
while ((ch = getchar()) != EOF && ch != '\n');
escrito = fprintf( pArchivo, "%d\t%s\t%lf\n", orden, nombre, salario );
}
if( escrito < 1 ){
printf( "\n ERROR!!! No se pudieron guardar los datos en el archivo."
"\n Pulse una tecla para continuar..."); getchar();
}
}while( n > 0 );
}
Un abrazso.
En el primer código cambia:
while( !feof(pArchivo) ){
fscanf(pArchivo,"%d\t%[^\n]\t%lf",&orden,nombre,&salario);
printf(" %d\t%-10s\t%lf",orden,nombre,salario);
}
por
fscanf(pArchivo,"%d\t%30[^\t]\t%lf",&orden,nombre,&salario);
while( !feof(pArchivo) ){
printf(" %d\t%-10s\t%0.2lf\n",orden,nombre,salario);
fscanf(pArchivo,"%d\t%30[^\t]\t%lf",&orden,nombre,&salario);
}
La cadena de control de fscanf ha cambiado nombre ahora recibirá un máximo de 30 caracteres, además se detendrá la lectura de nombre cuándo encuentre una tabulación (que es tu separador), no el carácter de nueva línea (que no puede existir en esa posición. También se cambia el orden de las lecturas en el buche, así no imprimirás por duplicado la última lectura.
6 - Buscar
Hola, muy buen día para todos. -
Tengo algo mal echo en la función strcmp que no logro discernir, estuve consultando en un montón de páginas y tal vez tenga que ver con cadena constantes y no pero no logro hacer que funcione, si lo pongo a mano por Ej.:
encontrado = strcmp( espaniol, "hogar" );
el resultado es correcto. -
MAFUS, maravilloso lo tuyo, funciona a la perfección. -
#include <stdio.h>
#include <string.h>
#define MAX_CAR 30
void buscarCampo( FILE *pArchivo );
int main( void ){
FILE *pArchivo = fopen( "../Escribir/pruebaE.txt", "r" );
if( pArchivo != NULL ){
buscarCampo( pArchivo );
fclose( pArchivo );
}else{
printf( "\n Error al abrir %s para lectura. Finaliza el programa.", "pruebaE.txt" );
}
return 0;
}
void buscarCampo( FILE *pArchivo ){
int encontrado=0, orden;
char ingles[MAX_CAR], espaniol[MAX_CAR], buscar[MAX_CAR], *p=NULL;
printf( "\n Intoduzca la palabra a buscar.....: " );
fgets( buscar, MAX_CAR, stdin );
if(( p=strchr(ingles, '\n' ))){
*p='\0';
}
while( !feof(pArchivo) ){
fscanf(pArchivo,"%d\t%30[^\t]\t%30[^\n]",&orden, ingles, espaniol);
encontrado = strcmp( espaniol, buscar );
if( encontrado != 0 ){
encontrado = strcmp( ingles, buscar );
}
if( encontrado == 0){
printf( "\n\n Orden Ingles Espaniol\n\n" );
printf(" %d\t%-30s\t%-30s\n",orden, ingles, espaniol);
break;
}
}
if( encontrado != 0 ){
printf( "\n No existe..." );
}
}
Saludos y muchas gracias. -
Línea 31:
Te has equivocado de puntero. En vez de ingles, debe ser buscar.
7 - Borrar registro
Hola, que tengan un muy buen día. -
El tema pasa ahora por borrar un registro del archivo y me gustaría que me den su criterio para hacer esta operación, yo he leído que hay 2 maneras de hacerlo, la primera consiste en tener un campo para indicar si el registro está borrado o no y en algún momento borrar todos los marcados para dicho fin, la segunda es hacer un bucle e ir copiando cada registro en un archivo temporal (salteando el elegido para borrar) y luego re nombrarlo con el nombre original, . -
Si tengo unos 15.000 registros cual de los 2 métodos utilizarían, o tal vez haya alguna otra manera más eficiente que desconozco. -
Una pregunta más, cual lejos está un archivo de transformarse en una tabla o base de datos. -
Saludos
CitarSi tengo unos 15.000 registros cual de los 2 métodos utilizarían, o tal vez haya alguna otra manera más eficiente que desconozco
Si tienes esa cantidad de registros yo utilizaria la primera opcion que dijiste, tener un campo donde este marcado si es registro activo, o eliminado.
El detalle esta en que dado que estas usando registros de longitud variable (Modo texto) y no modo binario es la opcion mas adecuada.
Tendrías que tener un programa adiconal que haga el trabajo pesado (Optimizar la base de datos) quitando los registros marcados como eliminados para NO ocupar mas espacio del necesario.
Con registros binarios (DE LONGITUD FIJA) es mas facil ya que es mas rapido dejar un segmento de X tamaño en ceros (NULL) y posteriormente ocuparlo con alguno de los registros nuevos.
Saludos!
8 - Ordenar
Hola, gente. -
Una vez más con una duda sobre archivos ahora binario, lo que quiero hacer es por cada ingreso que quede ordenado, también podría ser ordenar todo el archivo cada vez que sea necesario, en definitiva, lo que necesito es que me den alguna idea de como hacerlo y que tengan en cuenta que aproximadamente puede contener unos 15.000 registros. -
muchas gracias Alberto por las ideas del post anterior.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int habilitado;
char ingles[30];
char espaniol[30];
}Idiomas;
#define MAX_CAR 30
void menu( char *nombreArchivo );
long contar( char *nombreArchivo );
int contarMarcados( char *nombreArchivo );
Idiomas agregar( void );
void guardar( Idiomas idiomas, char *nombreArchivo );
void mostrar( char *nombreArchivo );
void buscar( char *nombreArchivo );
void modificarBorrar( FILE *file, Idiomas idiomas );
void limpiar( void );
void dibujo( void );
int main( void ){
char nombreArchivo[] = "traductor.bin";
menu( nombreArchivo );
return 0;
}
void limpiar( void ){
system("cls||clear");
}
long contar( char *nombreArchivo ){
FILE *file = fopen( nombreArchivo, "r" );
long numeroRegistros;
fseek(file, 0, SEEK_END); /* Nos situamos al final del mismo */
/* ftell devuelve el numero de bytes desde el principio del fichero
hasta la posicion actual que es el final del fichero */
numeroRegistros = ftell( file )/sizeof( Idiomas );
fclose( file );
return numeroRegistros;
}
int contarMarcados( char *nombreArchivo ){
int marcados = 0;
FILE *file = fopen( nombreArchivo, "rb" );
if( file != NULL ){
Idiomas idiomas;
rewind( file );
while( fread( &idiomas, sizeof(Idiomas), 1, file )){
if( idiomas.habilitado == 0 ){
marcados++;
}
}
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", nombreArchivo );
}
fclose( file );
return marcados;
}
void menu( char *nombreArchivo ){
int opc, ok, ch, marcados;
long numeroRegistros = 0;
do{
do{
limpiar( );
numeroRegistros = contar( nombreArchivo );
marcados = contarMarcados( nombreArchivo );
dibujo();
printf( "\n Cuenta hasta el momento con [%ld] palabra(s) traducida(s).", numeroRegistros );
printf( "\n Tiene [%d] registro(s) marcado(s) para borrar.", marcados);
dibujo();
printf( "\n =============== Menu principal ===============\n"
"\n [1] - Agregar"
"\n [2] - Listar"
"\n [3] - Buscar (-> Modificar -> Borrar)"
"\n [4] - Empaquetar"
"\n [5] - Finalizar"
"\n\n ingrese opcion.....:" );
ok = scanf( "%d", &opc ) == 1 && opc > 0 && opc <= 5;
while ((ch = getchar()) != EOF && ch != '\n');
}while( !ok );
switch ( opc ){
case 1: guardar( agregar(), nombreArchivo );
break;
case 2: mostrar( nombreArchivo );
break;
case 3: buscar( nombreArchivo );
break;
}
}while( opc != 5 );
}
Idiomas agregar( void ){
Idiomas idiomas;
char *p=NULL;
idiomas.habilitado = 1;
limpiar();
printf( "\n Introduzca palabra en ingles......:" );
fgets( idiomas.ingles, MAX_CAR, stdin );
if(( p=strchr( idiomas.ingles, '\n' )) ){ *p='\0'; }
printf( "\n Introduzca traduccion al español..:" );
fgets( idiomas.espaniol, MAX_CAR, stdin );
if(( p=strchr( idiomas.espaniol, '\n' )) ){ *p='\0'; }
return idiomas;
}
void guardar( Idiomas idiomas, char *nombreArchivo ){
FILE *file = fopen( nombreArchivo, "ab" );
if( file != NULL ){
fwrite( &idiomas, sizeof(Idiomas), 1, file );
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", nombreArchivo );
}
fclose( file );
}
void mostrar( char *nombreArchivo ){
FILE *file = fopen( nombreArchivo, "rb" );
limpiar();
if( file != NULL ){
Idiomas idiomas;
dibujo();
printf( "\n %-30s %-30s\n", "Ingles", "Espaniol" );
while( fread( &idiomas, sizeof(Idiomas), 1, file )){
printf( "\n %-30s %-30s", idiomas.ingles, idiomas.espaniol );
}
dibujo();
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", nombreArchivo );
}
printf( "\n\n Pulse una tecla para continuar..." ); getchar();
fclose( file );
}
void buscar( char *nombreArchivo ){
FILE *file = fopen( nombreArchivo, "rb" );
Idiomas idiomas;
char buscar[MAX_CAR], *p=NULL;
int encontrado;
limpiar();
if( file != NULL ){
printf( "\n Ingrese la palabra a buscar....: " );
fgets( buscar, MAX_CAR, stdin );
if(( p=strchr(buscar, '\n' )) ) { *p='\0';}
while( fread( &idiomas, sizeof(Idiomas), 1, file )){
encontrado = strcmp( idiomas.espaniol, buscar );
if( encontrado != 0 ){
encontrado = strcmp( idiomas.ingles, buscar );
}
if( encontrado == 0){
break;
}
}
if( encontrado != 0 ){
printf( "\n No existe..." );
printf( "\n\n Pulse una tecla para volver al menu..." ); getchar();
}else{
modificarBorrar( file, idiomas );
}
fclose( file );
}
else{
printf( "\n Error al abrir %s para lectura. Finaliza el programa.", nombreArchivo );
}
}
void modificarBorrar( FILE *file, Idiomas idiomas ){
int opc=0, ok, ch;
do{
limpiar( );
dibujo();
printf( "\n %-30s %-30s\n", "Ingles", "Espaniol" );
printf( "\n %-30s %-30s", idiomas.ingles, idiomas.espaniol );
dibujo();
printf( "\n\n ========== Modificar/borrar registro ==========\n"
"\n 1 - Modificar"
"\n 2 - Borrar"
"\n 3 - Continuar"
"\n\n ingrese opcion.....:" );
ok = scanf( "%d", &opc ) == 1 && opc > 0 && opc <= 3;
while ((ch = getchar()) != EOF && ch != '\n');
}while( !ok );
switch ( opc ){
case 1: ;
break;
case 2: ;
break;
case 3: break;
}
}
void dibujo( void ){
int con = 0;
printf( "\n" );
while( con <= 75 ){
printf( "-" ); con++;
}
}
Saludos.
Si lo que quieres es ordenarlos mientras lo vas guardando de uno en uno, tendrias que modifciar tu funcion guardar, hay algunos algoritmos de ordenamientos excelentes para tu aplicacion. El mas recomendado es el de biblioteca...
La idea basica es abstraer el sistema de Estantes usados en las bibliotecas para acomodar libros, algunos estantes estan llenos y otros a media capacidad Imaginemos que tenemos un sistema de registro (Donde esta el libro con X autor, donde empiezan las palabras con Z etc...)
La idea es por ejemplo, si vas a manejar un unico archivo, (ahorita vacio por el moemento) y llega una registro con la letra A o lo que sea... lo guardas en la posición 0, pero dejas libre la posición 1, esto es:
Tu archivo tendrá actualmente espacio para 2 registros (Posición 0 y posicion 1).
Llega un nuevo registro con la letra B, este lo colocas en la posición 2 y dejas la posicion 3 libre.
Entonces si en algun punto llega un nuevo registro con la letra A lo agregas en la posición 1, agregas un registro blanco al final y mueves los demás 1 registro hacia adelante cantidad de posiciones hacia adelante, y asi sucesivamente.
Tambien tendrias que tener un registro que te diga que la letra A empieza en X posicion y la B en tal posicion.
Lo anterior es la idea vaga
Mas info
https://es.wikipedia.org/wiki/Algoritmo_de_ordenamiento
https://es.wikipedia.org/wiki/Library_sort
CitarSupón que un bibliotecario almacene sus libros alfabéticamente en una estante, empezando por la A desde la izquierda, y continuando a la derecha a lo largo del estante sin espacios entre los libros hasta que termine por la Z. Si el bibliotecario adquiere un libro nuevo que pertenece a la sección B, una vez que encuentra el espacio correcto en la sección B, tiene que mover cada libro a partir de ese hasta el último libro en la sección Z para abrir espacio al libro nuevo. Esto es ordenación por inserción. Sin embargo, si dejara un espacio vacío después de cada letra, mientras hubiese un espacio vacío después de B, sólo tuviera que mover unos cuantos libros para poder hubicar el nuevo libro. Esto es el principio básico de Library Sort.
Hola, a todos.
Alberto, muy interesante lo que propones lo voy a llevar a la práctica para ver si lo logro, en realidad muy interesante. -
A continuación, dejo una manera que me vino a la mente y parece que puede dar resultado, lo dejo a su consideración para que me digan si lo puedo mejorar y que les parece la velocidad que se puede obtener al ordenar de esta manera. -
#include <stdio.h>
#include <string.h>
int main(void){
int numeros[10] = {0}, ordenar[10] = {0}, ingreso = 0;
int i, j;
for( i=0; i<10; i++ ){
printf(" \n Ingrese un entero (cero para finalizar)....: ");
scanf("%d", &ingreso);
if( ingreso == 0 ){
break;
}
numeros[i] = ingreso;
ordenar[i] = 1;
if( i > 0 ){
j=0;
while( j < i ){
if( ingreso < numeros[j] ){
ordenar[j] += 1;
}else{
ordenar[i] += 1;
}
j++;
}
}
}
printf("\n\n Numeros ==> ");
for( i=0; i<10; i++ ){
printf("%2d ", numeros[i]);
}
printf("\n Ordenar ==> ");
for( i=0; i<10; i++ ){
printf("%2d ", ordenar[i]);
}
return 0;
}
(http://i65.tinypic.com/2yw9z03.png)
Como comprenderán es solo una practica luego tengo que insertarlo en el programa de archivo binario. -
Saludos.
Hola esta bien tu código pero me pregunto, necesitas un arreglo para almacenar los registro y otro del mismo tamaño para saber en que posición se encontrarían ordenados? siendo muy estrictos realmente no es un método de ordenamiento ya que los datos siguen como en su estado original.
Pero bueno tomemos tu algoritmo como valido y comparemoslo frente a otros algoritmos amplia-mente documentados y como serian
bubble sort y Quick sort como se comporta
Para ello necesitamos algunas subrutinas que nos
midan el tiempo de procesamiento de nuestros algoritmos lamentablemente hacer esto no siempre es muy preciso y
depende del sistema operativo y arquitectura de la computadora donde se ejecuten estas pruebas.
La primera prueba que vamos a hacer es con tu algoritmo para ello lo encapsule en una función y decidí tomar algunas muestras
#include <stdio.h>
#include<windows.h>
#include <time.h>
double performancecounter_diff(LARGE_INTEGER *a, LARGE_INTEGER *b){
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return (double)(a->QuadPart - b->QuadPart) / (double)freq.QuadPart;
}
void rellenarOrdenarDatos(int numeros[],int ordenar[],int tamano){
int i,j,ingreso;
srand(time(NULL));
for( i=0; i<tamano; i++){
ingreso = rand()%100;
numeros[i]=ingreso;
ordenar[i] = 1;
if(i>0){
j=0;
while(j<i){
if( ingreso < numeros[j] ){
ordenar[j] += 1;
}
else{
ordenar[i] += 1;
}
j++;
}
}
}
}
int main()
{
const int TAMANO=15000;
int *arreglo=malloc(sizeof(int)*TAMANO);
int *ordenar=malloc(sizeof(int)*TAMANO);
LARGE_INTEGER t_ini, t_fin;
double secs;
QueryPerformanceCounter(&t_ini);
rellenarOrdenarDatos(arreglo,ordenar,TAMANO);
QueryPerformanceCounter(&t_fin);
secs = performancecounter_diff(&t_fin, &t_ini);
printf("%.16g milliseconds\n", secs * 1000.0);
free(arreglo);
free(ordenar);
return 0;
}
Entre las muestras que obtuve fueron estos tiempos en milisegundosCitar937.9348
1006.7837
984.6807
917.7779
1013.7831
993.5917
Ahora la prueba con BubbleSort
#include <stdio.h>
#include<windows.h>
#include <time.h>
double performancecounter_diff(LARGE_INTEGER *a, LARGE_INTEGER *b){
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return (double)(a->QuadPart - b->QuadPart) / (double)freq.QuadPart;
}
void rellenarArreglo(int numeros[],unsigned int tamano){
srand(time(NULL));
int i=0;
for(i=0; i<tamano; i++)
{
int r = rand()%100+1;
numeros[i]=r;
}
}
void bubbleSort(int numeros[],int tamano){
int i,j,temp;
for (i=1; i<tamano; i++){
for(j=0 ; j<tamano - 1; j++){
if (numeros[j] > numeros[j+1]){
temp = numeros[j];
numeros[j] = numeros[j+1];
numeros[j+1] = temp;
}
}
}
}
int main()
{
const int TAMANO=15000;
int *arreglo=malloc(sizeof(int)*TAMANO);
LARGE_INTEGER t_ini, t_fin;
double secs;
QueryPerformanceCounter(&t_ini);
rellenarArreglo(arreglo,TAMANO);
bubbleSort(arreglo,TAMANO);
QueryPerformanceCounter(&t_fin);
secs = performancecounter_diff(&t_fin, &t_ini);
printf("%.16g milliseconds\n", secs * 1000.0);
free(arreglo);
return 0;
}
Utilizando el algoritmo
bubble sort el tiempo de procesamiento fue mas alto por ende menos optimo aproximadamente 1500 milsegundos en promedio, en este caso te debo los tiempos exactos
Por ultimo utilize el algoritmo
Quick Sort y la diferencia fue abismal
El siguiente codigo es similar al anterior salvo por el metodo de ordenamiento
void qs(int lista[],int limite_izq,int limite_der)
{
int izq,der,temporal,pivote;
izq=limite_izq;
der = limite_der;
pivote = lista[(izq+der)/2];
do{
while(lista[izq]<pivote && izq<limite_der)izq++;
while(pivote<lista[der] && der > limite_izq)der--;
if(izq <=der)
{
temporal= lista[izq];
lista[izq]=lista[der];
lista[der]=temporal;
izq++;
der--;
}
}while(izq<=der);
if(limite_izq<der){qs(lista,limite_izq,der);}
if(limite_der>izq){qs(lista,izq,limite_der);}
}
void quicksort(int lista[],int n)
{
qs(lista,0,n-1);
}
int main()
{
const int TAMANO=15000;
int *arreglo=malloc(sizeof(int)*TAMANO);
LARGE_INTEGER t_ini, t_fin;
double secs;
QueryPerformanceCounter(&t_ini);
rellenarArreglo(arreglo,TAMANO);
quicksort(arreglo,TAMANO);
QueryPerformanceCounter(&t_fin);
secs = performancecounter_diff(&t_fin, &t_ini);
printf("%.16g milliseconds\n", secs * 1000.0);
free(arreglo);
return 0;
}
Aqui una lista de tiempos obtenidosCitar2.1082
2.2769
2.1349
2.1203
2.0972
2.1325
En
promedio 2 mili-segundos el tiempo de procesamiento
En conclusion yo te recomendaria usar une metodo de ordenamiento ya que ademas de que evitas reservar memoria inecesaria el performance es mejor.SI hay dudas o no estas de acuerdo con algo no dudes en hacermelo saber.
Saludos.... :silbar:
Hola, crack81, como estas.
Tal vez no leíste por donde venia el tema o no me supe explicar en el post anterior. -
CitarHola esta bien tu código pero me pregunto, necesitas un arreglo para almacenar los registro y otro del mismo tamaño para saber en que posición se encontrarían ordenados? siendo muy estrictos realmente no es un método de ordenamiento ya que los datos siguen como en su estado original.
Lo que quiero lo vas a entender mejor en la imagen que dejo a continuación, es para un traductor inglés/español y ordenándolo de esta manera por Ej. no necesito hacer un archivo temporal o en memoria para hacer que se muestre en un determinado orden, puedo ordenar el archivo por las palabras en inglés, en español y de forma natural, como se ingresaron los datos. -
Ahora desconozco si esto es más lento o rápido que tener los datos ordenados físicamente, lo que estoy seguro es que no necesito 5 archivos por si hay 5 campos por ordenar, simplemente se agregan tantas columnas como sea necesarias y listo. -
De cualquier manera por el momento no deseo competir con creadores de bases de datos, aunque debo reconocer que es mi fuerte. -
(http://i63.tinypic.com/2i1nx1f.png)
Saludos, un abrazo.
Tu idea esta bien, te sirve bastante si no quieres estar moviendo los registros "fisicamente" en su posicion del archivo.
Asi solo guardas en un arreglo externo que el registro X del archivo esta en el orden TAL.
Y podrias implementar una busqueda casi binaria por ejemplo tienes 1000 Registros y buscas por la letra T o no se la palabra tocar, vez quien es el registro 500 y si la palabra tocar es mayor te desplazas 250 registros mas adelante y buscas en el registro 750 y si es mayor o menor te desplazas 125 registros adelante o atras y asi sucesivamente. Para buscar rapidamente la palabra tal....
Saludos!
Hola, a todos. -
Créanme que probé de muchas maneras pero no me funciona, el error lo tengo en el último campo, no puedo hacer que se modifique, les dejo solamente la función guardar para no hacerlo tan largo, descarto que alguien de Uds. me podrá dar la solución sin tener que correr el programa. -
Alberto me interesa lo de la búsqueda casi binaria, lo voy a practicar. -
void guardar( Idiomas idiomas, char *nombreArchivo, long numeroRegistros ){
FILE *file = fopen( nombreArchivo, "ab" );
Idiomas tmp;
tmp.habilitado = idiomas.habilitado;
strcpy( tmp.ingles, idiomas.ingles );
strcpy( tmp.espaniol, idiomas.espaniol);
tmp.orden = 1;
if( file != NULL ){
numeroRegistros += 1;
if( numeroRegistros > 1 ){
fseek( file, 0, SEEK_SET );
while( fread( &idiomas, sizeof(Idiomas), 1, file )){
if( strcmp( tmp.ingles, idiomas.ingles ) < 0 ) {
idiomas.orden += 1;
fwrite( &idiomas, sizeof(Idiomas), 1, file );
}else{
tmp.orden += 1;
}
}
}
fwrite( &tmp, sizeof(Idiomas), 1, file );
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", nombreArchivo );
}
fclose( file );
}
(http://i65.tinypic.com/2j3qvsm.png)
Saludos y desde ya muchas gracias. -
No estoy seguro de cuales son los datos ingresados pero el problema puede ser el strcmp, ya que solo lo tienes mejor que cero y strcmp puede devolver -1, 0 o 1 segun sea menor, igual o mayor deberias de cambiarlo por !=
strcmp( tmp.ingles, idiomas.ingles ) != 0
Saludos!
Hola. -
alberto creo que el problema era la apertura del archivo y en realidad nunca se ejecutaba el bucle, después de tanta práctica logre hacer que funcionara pero o sorpresa cada vez que tenia que modificar el orden de la palabra grabada en el archivo se duplicaba y en realidad lo que necesitaba era modificarla, ahora el problema que tengo es que no logro regresar el puntero file al comienzo de la linea donde está parado para modificarla/sobre escribirla. -
file -= sizeof( Idiomas ); //es algo que se me antojo de tanto practicar, pero no me funciono. -
¿Me podrían decir porque la siguiente línea no mueve el cursor del archivo al comienzo de la misma?
fseek(file, -1L*sizeof(Idiomas), SEEK_CUR);
void guardar( Idiomas idiomas, char *nombreArchivo, long numeroRegistros ){
FILE *file = fopen( nombreArchivo, "a+b" );
int i=0;
if( file != NULL ){
numeroRegistros += 1;
if( numeroRegistros > 1 ){
Idiomas tmp;
tmp.habilitado = idiomas.habilitado;
strcpy( tmp.ingles, idiomas.ingles );
strcpy( tmp.espaniol, idiomas.espaniol);
tmp.orden = 1;
fseek (file, 0L, SEEK_SET);
while( fread( &idiomas, sizeof(Idiomas), 1, file) ){
i = strcmp( tmp.ingles, idiomas.ingles );
if( i < 0 ){
idiomas.orden += 1;
printf("\n antes...: %p", file);
file -= sizeof( Idiomas );
// fseek(file, -1L*sizeof(Idiomas), SEEK_CUR);
printf("\n despues.: %p", file); getchar();
fwrite( &idiomas, sizeof(Idiomas), 1, file );
}else{
tmp.orden += 1;
}
}
fwrite( &tmp, sizeof(Idiomas), 1, file );
}else{
idiomas.orden = 1;
fwrite( &idiomas, sizeof(Idiomas), 1, file );
}
}else{
printf( "\n Error al abrir %s para escritura/lectura. Finaliza el programa.", nombreArchivo );
}
fclose( file );
}
Saludos.
Nunca lo he provado de esa forma prefiero manejarlo desde el SEEK_SET para hacerlo desde el inicio del archivo. lo que me gusta hacer es tener un contador para saber cual es el numero de registro actual y si quiero regrersar al que previamente lei lo que hago es
fseek(file, (contador-1)*sizeof(Idiomas), SEEK_SET);
Tendria que probar con la posición SEEK_CUR como lo mencionas.
Saludos!
Hola. -
una consulta, si tengo un archivo con palabras desordenadas y luego las ordeno en un arreglo de estructura, como debo hacer para escribir en el archivo nuevamente, tengo que borrarlo primero o puedo sobrescribir registro por registro. -
desde ya muchas gracias.
De las 2 formas es valido.
Si es la misma cantidad de registros no hay por que preocuparse...
Se mueve a la posicion 0 con fseek y se escribe registro por registro.
En caso de que quieraa reescribir (borrar y escribir)
Solo hay que cerrar el archivo actual y abrir uno nuevo con el mismo nombre y en modo "w" o "wb" segun sea de texto o binario y asi escribir registro por registro en el archivo nuevo.
Saludos