Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Es posible trabajar con un fichero de estructura variable? (https://www.clubdelphi.com/foros/showthread.php?t=46332)

xaguilars 29-07-2007 00:00:17

Es posible trabajar con un fichero de estructura variable?
 
Hola tengo un problemilla aver si me podéis ayudar...
Estoy haciendo una aplicación para gestionar MP3 además de info relacionada con el álbum y los artistas...

Trabajo con registros (record) porque aun no sé utilizar bases de datos. Quisiera saber si hay una manera de hacer lo siguiente:

Código Delphi [-]
type
  TSongInfo = Record
      Title: string [255];
      {etc.}
  end;
  TAlbumInfo = array of TSongInfo;
  TDatafile = File of TAlbumInfo;

Esta estructura como tal, según mi delphi 2006, no está permitida porque debería definir un límite en la matriz para poder utilizar TAlbumInfo en un fichero de registros.

Código Delphi [-]
type
  TSongInfo = Record
      Title: string [255];
      {etc.}
  end;
  TAlbumInfo = array [0..19] of TSongInfo;
  TDatafile = File of TAlbumInfo;
Ahora sí me deja compilar. No da error en la estructura de datos. Un álbum podrá tener máximo 20 canciones. Pero el problema es... ¿qué pasa si tengo álbums de más de 20 canciones?

Alguien me puede dar una solución? Gracias!

Delphius 29-07-2007 07:11:59

Hola xaguilars,
¿Estás seguro de que puedes hacer un archivo de arrays de registros? Yo hice la prueba... pero me resulta extraño el heho de que el compilador acepte algo como lo que tu dices.
Se puede hacer un file of records, ¿pero... un file de arrays? Como dije... no hice la prueba...

Para darte mayor libertad al asunto de guardar una cantidad arbitraria de canciones, yo creo que podrías considerar el uso de un TStringList. Ya que cuenta con LoadFromFile() y SaveToFile() que lo que hace es abrir y guardar el contenido de un archivo.

Podrías valerte del empleo de una "marca" para determinar cuando termina un album y empieza otro.
Podrías hacer algo como:

*ALBUM:Un nombre#20
$Info cancion 1
$Info cancion 2
...
$cancion20

La idea es que *ALBUM sea como cabecera del album, los dos puntos una marca para indicar que lo que sigue es el nombre del album, y # correspondería a la marca que establece la cantidad de canciones que tiene el album.
Para cada canción del album, siguiendo con el ejemplo: se indica que su "cabecera" es el símbolo $.

Es una alternativa,... no es la mejor... pero a lo mejor te resulta.
Yo hice algo parecido... para hacer un manejo de una playlist (ultima versión). Chequealo por si te interesa.

Saludos,

dec 29-07-2007 07:52:35

Hola,

Si se trata de almacenar información en archivos y no usar bases de datos ahí están los formatos INI y XML. No se reinventa la rueda y por otro lado da juego a que estos archivos puedan leerse no sólo desde nuestra aplicación, por ejemplo, sino por cualquier progama capaz de procesar estos formatos. Vamos, digo yo, ¿eh? :)

Delphius 29-07-2007 16:12:21

Hola Dec, también estuve pensando en la posibilidad de un archivo INI... Y puede que sea recomendable. Al menos yo lo imagino algo así:

[ALBUM 1]
Cantidad=20
Cancion 1=titulo canción 1#Artista$Duracion@etc...
Cancion 2=titulo canción 2#Artista$Duracion@etc...
....

Aunque aquel ejemplo de manejo de PlayList que hice hace ya bastante tiempo no es tan diferente a la manera en que trabaja un archivo INI.
Mi idea, tal vez un poco descabellada, es guardar toda la info de cada canción en una línea. Una vez que se invoca a ReadString(), mediante alguna función o procedimiento leer cada "marca" y extraer la información entre ellas.

Habría que ver que dice xaguilars.

dec 29-07-2007 16:41:31

Hola,

Sí; tal vez un archivo INI no sería lo más adecuado, pero, ¿qué me dices de un archivo XML? Ahí puedes incluso inventarte tu propia "estructura del documento". Tal vez por ahí pudieran ir los tiros, pero, también como tú dices, y, en todo caso, a ver qué dice el compañero que inició este Hilo.

Delphius 29-07-2007 17:13:56

Cita:

Empezado por Dec
¿qué me dices de un archivo XML?

Se que un XML es un archivo al que se puede diseñar su propia estructura, fuera de eso no se nada:(.
Como no lo empleo y no termino de comprender sus propósitos no opino mucho.
He visto muchos archivos XML pero de comprender su funcionamiento no puedo decir. Tal vez les parezca un poco tonto pero me cuesta entender como y para que usarlos:o.

Saludos,

xaguilars 29-07-2007 19:26:47

Gracias por vuestras propuestas, no me esperaba q me respondieran tan rápido jeje. La verdad es que me han ayudado mucho a pensar y aquí tengo una posible solución...

Código Delphi [-]
 
Type

TSong = packed record
  {EXTERNAL MP3 INFO}
    ID, {POSITION IN DYNAMIC LIST}
    KID:Integer; {KEY IDENTIFIER, UNIQUE}
    Path, {MP3 FILENAME}
    Lyrics: string[255];  {TXT file PATH}
    Rate:0..5; {NUMBER OF STARS}
    Favourite:Boolean;
  {ID3 Tags, INTERNAL MP3 INFO}
    Title,
    Artist,
    Album: string[255];
    Track: Byte;
    Year: string[4];
    Genre,
    Comments,
    Composer,
    Encoder,
    Copyright,
    Link,
    Language: string[255];
  end;

  TAlbum = packed record
    Name: string[255];
    Rate:0..5; {NUMBER OF STARS}
    Favourite:Boolean;
    Songs: String[255]; {ex.: 'KID:#157#158#159#160#7#2514#12477'}
  end;
 
  TSongFile: File of TSong;
  TAlbumFile: File of TAlbum; //Ahora sí podría guardarlo como registros
 
  //ALBUM LIST
  PAlbumNode= ^TAlbumNode; {pointer to TAlbumNode}
 
  TAlbumNode = record
    Info:TAlbum;
    Next:PAlbumNode;
  end;
 
 //SONG LIST
  PSongNode= ^TSongNode; {pointer to TSongNode}

  TSongNode = record
    Info:TSong;
    Next:PSongNode;
  end;
 
(*TAlbum2 = record  {En este caso usaría punteros a las canciones en vez de un string con los KID, pero no sé qué será más eficiente a la hora de programar y de recursos de memoria}
    Name: string[255];
    Rate:0..5; {NUMBER OF STARS}
    Favourite:Boolean;
    Songs: array of PSongNode;
  end;
 PAlbumNode= ^TAlbumNode; {pointer to TAlbumNode}
 
  TAlbumNode = record
    Info:TAlbum2;
    Next:PAlbumNode;
  end;*)

Aquí os dejo un link con el esquema de los dos casos. Aún no lo he implementado. He ido escribiendo conforme me han surgido ideas... pero cuando lo intente os digo que tal... o si me podéis aconsejar antes mejor ;)
En cuanto a lo de XML no sé cómo funciona ni qué posibilidades tiene... Delphi lo trata como un fichero de texto? O puede interpretar el código?

Bueno gracias por todo!

dec 29-07-2007 19:38:27

Hola,

A partir de Delphi 6 (me parece) se cuenta con el componente "TXmlDocument", así como una serie de clases e interfaces de apoyo. Con estas herramientas es posible procesar archivos XML, insertar datos, obtenerlos, actualizarlos, etc.

Además de el componente mencionado, a poco que busques en sitios como Torry's, encontrarás componentes para trabajar con XML desde Delphi. Pero, vamos, que es una opción más, que a lo mejor te basta y sobra como lo estás haciendo. :)

cHackAll 30-07-2007 10:27:35

1 Archivos Adjunto(s)
Vaya, si que le dieron muchas soluciónes mientras yo estaba en "otras"; acá les doy la mía que tiene mi estilo clásico: :cool:

En realidad no entiendo muy bien cuál es el problema? Incrementar (duplicar o triplicar) el tamaño del vector TAlbumInfo, no es ningun problema tomando en cuenta que el espacio de cada estructura es menor a un Kb. Ahora si el problema es el espacio que utiliza en disco tampoco lo veo muy problemático; si usaras una BD el espacio ocupado por ésta sobrepasaría los 100 Kb. SIN datos.

Sin embargo para ahorrar algo de tamaño te aconsejaría utilizar un archivo sin "tipo", por ejemplo; abres tu pseudo BD, lees el primer caracter (Byte), el cual representará la cantidad de Albunes que tienes; luego entras a un bucle en el que lees un byte y en éste obtienes el tamaño del primer Track; lees el siguiente (segundo) y así susesivamente, claro que es mi perspectiva, y la adecúo un poco para fines genéricos:

Código Delphi [-]
uses Windows;
type
 PTrack = ^TTrack;                                             // TTrack de tipo Puntero
 TTrack = packed record                                        // Tu estructura, parecida al ID3v1 de los MP3s
  Number: Byte;
  Title: string [30];
  Artist: string [30];
  Album: string [30];
  Year: Word;
  Comment: string [30];
  Genre: Byte;
 end;
 
var
 Dummy: Cardinal;                                              // Por optimización Dummy es global
 Items: packed record                                          // Items es el formato en sí de tu "BD"
  Count: Cardinal;                                             // Utilizo una variable de 32 bits; por optimización y el posible uso del ejemplo en otros casos.
  Item: array [0..255] of PTrack;                              // Si te molesta que haya un vecor muy "grande" o inutil; pues lo que se hace es un vector de
 end;                                                          // punteros los cuales son asignados individualmente cada vez que se agrega un registro nuevo.
 
function NewItem: PTrack;                                      // Para asignar/crear un nuevo registro al final del vector
begin
 New(Result);                                                  // Asignamos en memoria un nuevo registro,
 Items.Item[Items.Count] := Result;                            // Lo guardamos en el vector...
 Inc(Items.Count);                                             // e incrementamos el valor que indica la cantidad de elementos del vector.
end;
 
function DelItem(Index: Cardinal): LongBool;                   // Para borrar un registro del vector
begin
 Result := Index < Items.Count;                                // Verificamos si el índice introducido está fuera de rango
 if not Result then Exit;
 Dispose(Items.Item[Index]);                                   // Liberamos la memoria anteriormente asignada...
 while Index < Items.Count do
  begin
   Items.Item[Index] := Items.Item[Index + 1];                 // Vamos recorriendo los elementos del vector para tapar el "hueco" generado...
   Inc(Index);
  end;
 Dec(Items.Count);                                             // y decrementamos nuestro contador
end;
 
function Save(lpFileName: PChar): LongBool;
var hFile, Index: Cardinal;
begin
 hFile := CreateFile(lpFileName, GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0); // Creamos / pisamos la "BD"
 Result := hFile <> INVALID_HANDLE_VALUE;
 if not Result then Exit;
 Index := 0;
 WriteFile(hFile, Items.Count, 4, Dummy, nil);                 // Guardamos en nuestro archivo el tamaño del vector
 while Index < Items.Count do
  begin
   WriteFile(hFile, Items.Item[Index]^, SizeOf(Items.Item[0]^), Dummy, nil); // Guardamos los elementos del vector
   Inc(Index);
  end;
 CloseHandle(hFile);
end;
 
function Open(lpFileName: PChar): LongBool;
var hFile, Index: Cardinal;
begin
 hFile := CreateFile(lpFileName, GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0); // Abrimos la "BD"
 Result := hFile <> INVALID_HANDLE_VALUE;
 if not Result then Exit;
 ReadFile(hFile, Index, 4, Dummy, nil);
 while Items.Count < Index do
  ReadFile(hFile, NewItem^, SizeOf(Items.Item[0]^), Dummy, nil);
 CloseHandle(hFile);
end;
 
begin
 Open('pseudoBD.dat');
 if Items.Count >= 4 then
  DelItem(0);
 with NewItem^ do                                              // La forma de acceso tambien se hace sencilla:
  begin
   Number := 1;
   Title := 'Allegro Club Delphi';
   Artist := 'cHackAll';
   Year := 2007;
  end;
 with NewItem^ do                                              // Añadimos otro...
  begin
   Number := 2;
   Title := 'Sinfonía Nº1 Delphiana';
   Artist := 'Moderadores';
   Year := 2008;
  end;
 Save('pseudoBD.dat');
end.

PD: Utilizando una BD; no tendrás que preocuparte por los registros...; integridad, duplicidad, etc, etc, etc... (tampoco de componentes)

xaguilars 30-07-2007 18:28:36

Hola cHackAll, gracias por tu propuesta. No sé si he entendido mal pero propones utilizar un fichero con un header o cabezera... me parece interesante e igual después estudio como adaptarlo a mi caso. Ayer estuve mejorando mi estructura de datos y me ha quedado de la siguiente manera (en realidad el programa ya estaba funcionando, pero estoy reescribiendo el código porque he añadido la función de manejar álbums que antes no tenía. Era el proyecto final de 1º de FP de Admin de sistemas jeje, ahora tengo k empezar 2º y allí empezamos con las BDD). Bueno ahí va:


Código Delphi [-]
unit UEstructuras;

interface
uses classes, sysutils;

type
  {----------SONGS----------}
  TSong = packed record
  {EXTERNAL MP3 INFO}
    UID:Integer; {UNIQUE IDENTIFIER}
    Path, {MP3 FILENAME}
    Lyrics: string[255];  {TXT file PATH}
    Rate:byte; {NUMBER OF STARS}
    Favourite:Boolean;
    {ID3 Tags, INTERNAL MP3 INFO}
    Title,
    Artist,
    Album: string[255];
    Track: Byte;
    Year: string[4];
    Genre,
    Comments,
    Composer,
    Encoder,
    Copyright,
    Link,
    Language: string[255];
  end;

  TSongFile= File of TSong;

  PSongNode= ^TSongNode; {pointer to TSongNode}

  TSongNode = record
    Prev:PSongNode;
    ID:Integer;
    Info:TSong;
    Next:PSongNode;
  end;

  {----------ALBUMS----------}
  TAlbum = packed record
    UID:Integer; {UNIQUE IDENTIFIER}
    Name: string[255];
    Rate:byte; {NUMBER OF STARS}
    Image: string[255]; {Path of Image}
    Favourite:Boolean;
    Songs: String[255]; {ex.: 'UID:#157#158#159#160#7#2514#12477'}
  end;

  TAlbumFile= File of TAlbum;

  PAlbumNode= ^TAlbumNode; {pointer to TAlbumNode}

  TAlbumNode = record
    Prev:PAlbumNode;
    ID:Integer;
    Info:TAlbum;
    Next:PAlbumNode;
  end;

  {----------ARTISTS----------}
  TArtist = packed record
    UID:Integer; {UNIQUE IDENTIFIER}
    Name, {KEY IDENTIFIER, UNIQUE}
    Country,
    Language,
    Genre,
    Web,
    Comment,
    Albums: string [255]; {ex.: 'UID:#157#158#159#160#7#2514#12477'}
    Photos : array [0..13] of string [255];   {Path of each photo}
  end;

  TArtistFile= File of TArtist;

  PArtistNode= ^TArtistNode; {pointer to TArtistNode}

  TArtistNode = record
    Prev:PArtistNode;
    ID:Integer;
    Info:TArtist;
    Next:PArtistNode;
  end;

  TSearchMethod = (ID, UID, Path, Name);
 
{***********CLASSES***********}
  TSongs = class
     private
      Datapath:string;                              // Ruta al archivo de registros
      function UIDAssign: integer;                  // Asigna el primer número disponible que no se use a los campos UID
    public
      List:PSongNode;                                // Puntero a la lista de canciones
      Counter:Integer;                              // Contador de elementos de la lista
      constructor Create (Filename:string);          // Crea el objeto y llama a LoadDatabase
      procedure LoadDatabase;                        // Carga los registros en la lista de memoria
      procedure SaveDatabase;                        // Guarda la lista en el fichero de registros. Sobreescribe todo.
      function Add (Info: TSong):boolean;            // Añade un nodo, una canción
      function Delete (ID: Integer):boolean;        // Elimina una canción
      function IDReset : boolean;                    // Cuando por ejemplo se elimina un nodo, se reasignan los ID
      procedure DelBrokenLinks;                      // Si una canción no se encuentra en el PC, se da la opción de eliminar el registro
      function Search (Info:TSong; Option:TSearchMethod): PSongNode;  // Busca una canción en la lista dado un criterio de búsqueda
      procedure DropList;                            // Vacía la lista
      procedure FillStringList (Target: TStrings);  // Llena un TStrings con los elementos de la lista
      destructor Destroy; overload;                  // Guarda la lista en el fichero, destruye el objeto y la lista
  end;

  TAlbums = class
    private
      Datapath:string;
      function UIDAssign: integer;
    public
      List:PAlbumNode;
      Counter:integer;
      constructor Create (Filename:string);
      procedure LoadDatabase;
      procedure SaveDatabase;
      function Add (Info: TAlbum):boolean;
      function Delete (ID: Integer):boolean;
      function IDReset : boolean;
      function Search (Info:TAlbum; Option:TSearchMethod): PAlbumNode;
      procedure DropList;
      procedure FillStringList (Target: TStrings);
      destructor Destroy; overload;
  end;

  TArtists = class
    private
      Datapath:string;
      function UIDAssign: integer;
    public
      List:PArtistNode;
      Counter:integer;
      constructor Create (Filename:string);
      procedure LoadDatabase;
      procedure SaveDatabase;
      function Add (Info: TArtist):boolean;
      function Delete (ID: Integer):boolean;
      function DeleteByName (Name:String):boolean;
      function IDReset : boolean;
      function Search (Info:TArtist; Option:TSearchMethod): PArtistNode;
      procedure DropList;
      procedure FillStringList (Target: TStrings);
      destructor Destroy; overload;
  end;

Si os habéis fijado tengo algunos tipos y clases que sólo difieren del tipo de una variable o del tipo que recibe o devuelve un método. He probado de hacer una clase global y después subclases... pero sería lo mismo porque tendría que redefinir... ya que cada método de cada clase utiliza implementaciones distintas, aunque sea sólo cambiar un tipo. No sé hasta qué punto lo puedo simplificar todo.

Me gustaría aprender BDD en delphi, porque si yo supiera haría esto como BDD... pero no sé por donde empezar hay tantas cosas... qué me aconsejáis?

Ah se me olvidaba... si quiero salir de un bucle basta poner 'break;'?

Gracias gente.

cHackAll 31-07-2007 05:09:03

Bueno, las BDs son como várias hojas de Excel, en cada una de ellas guardas datos como matriz. La gran diferencia entre ésto y una BD como tal (aparte de la obvias) es que cada columna puede tener un formato diferente... a a qué me refiero con ésto: a que puedes guardar en la primera un numero que incremente solo, en la segunda un entero de 32 bits, en la tercera una cadena y en la cuarta una fecha... (ejemplo ehh?)

Lastimosamente en breve me debo ir así que te lo mostraré con un ejemplo mañana.

PD: revisa el Minitutorial de Caral; pues esta "Gueno"...

Saludos.


La franja horaria es GMT +2. Ahora son las 05:32:06.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi