Recent

Author Topic: need help with show a file like this  (Read 40864 times)

GMP_47

  • Jr. Member
  • **
  • Posts: 60
need help with show a file like this
« on: September 20, 2015, 10:23:12 pm »
hello, I'm not sure if I should post this here. I use Boodshed Dev-Pascal v1.9.2 (freepascal) is what we where encouraged to use.
I've been looking everywhere but can't seem to find info on how to make this work:
they gave me a .dat and I have to code something to display it on a DOS window (C:\>Mostar.exe DEMO.dat [ENTER])

.dat structure:
Bytes  data type    description
2         Integer      serial
n         CString      fullname+(path)
2         Date         last access date
2         Integer      number of fields per register
m*(1) RegType      definition of field
2         Integer      # registers
t*(2)   RegData      reg with data

(1) multiplied by the # of register fields
(2) multiplied by the # of registers in the archive

----[CONTENIDO DEL ARCHIVO]--------------------
Nro. de Serie: 43112               //serial
Full Filename: C:/DEMO.dat      //fullname+(path)
Fecha Modificacion: 2014/9/20  //last access date
Cantidad de Campos Customizados: 4  //number of fields per register
Campo [codigo: 1, descripcion: Nombre]     //definition of field (code+descr)
Campo [codigo: 2, descripcion: Telefono]    //definition of field (code+descr)
Campo [codigo: 3, descripcion: Direccion]   //definition of field (code+descr)
Campo [codigo: 4, descripcion: EMail]        //definition of field (code+descr)
Cantidad de Registros: 5          //# of total regs
-----------------------
Nombre: Rogelio Roldan
Telefono: 4532-2411
Direccion: Rulo 126
EMail: roge@roldan.com.ar
-----------------------
Nombre: Mongo Piccio
Telefono: 4223-2345
EMail: mongo@piccio.com.uy
-----------------------
Nombre: Donald Patto
EMail: pattodonald@gmail.com
-----------------------
Nombre: Roviralta Cenizza
Telefono: 5423-7732
Direccion: Pje. Lulupo 4645
EMail: cenicero@rovi.com
-----------------------
Nombre: Momo Lulubell
Telefono: 4212-6623
Direccion: Formosa 4112
EMail: momo@saynomore.com
----[FIN CONTENIDO DEL ARCHIVO]-----------------

types
RegType

Bytes data type    Descr
2     Integer        . field code
n     CString          Description field

RegData

Bytes          data type   Description
2                 Integer      # completed fields
                   Integer      Code
(2+n)*(1)   CString       Content

CString
just a smallint

date
a date in 2 bytes
(7bits:year/4bits:month/5bits:day)


attachments:
the .dat to show (EXAMEN2.DAT)
my code in .pas

thank you
« Last Edit: October 11, 2015, 09:40:27 pm by GMP_47 »

eny

  • Hero Member
  • *****
  • Posts: 1646
Re: need help with create a file like this
« Reply #1 on: September 20, 2015, 11:42:57 pm »
Another one for the homework service.
All the information you need is here: http://wiki.lazarus.freepascal.org/File_Handling_In_Pascal#Binary_files
All posts based on: Win10 (Win64); Lazarus 3_4  (x64) 25-05-2024 (unless specified otherwise...)

GMP_47

  • Jr. Member
  • **
  • Posts: 60
Re: need help with create a file like this
« Reply #2 on: September 21, 2015, 12:04:13 am »
Another one for the homework service.
All the information you need is here: http://wiki.lazarus.freepascal.org/File_Handling_In_Pascal#Binary_files

forgive me but it says "Classes"? I dont know OOP.
I also tested out  FilePos and FileSize but the DOS window closes after executing, even with Readkey; .
thanks for the quick reply but I did not understand any of the article.

shobits1

  • Sr. Member
  • ****
  • Posts: 271
  • .
Re: need help with create a file like this
« Reply #3 on: September 21, 2015, 12:14:45 am »
just how did you identify the structure of the file???
did they describe it to you or did you think it yourself.

any way (although I think you are way-off) from what I gather the file structure look like this:
the file consist of two parts a header and data parts

HEADER
----------
number_of_entries(+2) : byte (it may use integer if number of entries should > 253)
'=' : char (constant, I think it used to define the size of the previous variable)
data length (length of the path) : byte
data/path : string
date : 4 byte

I'm not sure why it adds 2 to the number of entries  %)

DATA
------
delimiter : byte = null or 0x00
column id: byte
data length : byte
data : string

the DATA is stored sequentially so DATA1 then DATA2 then DATA3

Code: [Select]
Type
  Header = record
     nEntry : byte ; { byte for this data file}
     equ : char ;
     path_length : byte ;
     path : string ;
     date : Longint;
  End;

  Data = record
     delimiter : byte;
     column_id : byte;
     data_length: byte;
     data : string;
  End;

read the header first then loop through the data (read data column by column and skip to new line when detecting column_id=1 ).

write the file should not be difficult using the same structure.

GMP_47

  • Jr. Member
  • **
  • Posts: 60
Re: need help with create a file like this
« Reply #4 on: September 21, 2015, 01:30:11 am »
just how did you identify the structure of the file???
did they describe it to you or did you think it yourself.

any way (although I think you are way-off) from what I gather the file structure look like this:
the file consist of two parts a header and data parts

HEADER
----------
number_of_entries(+2) : byte (it may use integer if number of entries should > 253)
'=' : char (constant, I think it used to define the size of the previous variable)
data length (length of the path) : byte
data/path : string
date : 4 byte

I'm not sure why it adds 2 to the number of entries  %)

DATA
------
delimiter : byte = null or 0x00
column id: byte
data length : byte
data : string

the DATA is stored sequentially so DATA1 then DATA2 then DATA3

Code: [Select]
Type
  Header = record
     nEntry : byte ; { byte for this data file}
     equ : char ;
     path_length : byte ;
     path : string ;
     date : Longint;
  End;

  Data = record
     delimiter : byte;
     column_id : byte;
     data_length: byte;
     data : string;
  End;

read the header first then loop through the data (read data column by column and skip to new line when detecting column_id=1 ).

write the file should not be difficult using the same structure.

why number_of_entries?
the sructure was given by them. I translated it the best I could.
your code gave me an idea. it doesnt compile though. please advise.
thanks.

rvk

  • Hero Member
  • *****
  • Posts: 6675
Re: need help with create a file like this
« Reply #5 on: September 21, 2015, 09:30:41 am »
why number_of_entries?
the sructure was given by them. I translated it the best I could.
Are you sure they didn't just give you the structure as an example. If I look in the binary file I see that the fields are noted in the beginning of the binary file (see attached image). So my guess is you need to first extract the fields (from that header) and after that you read the records (like shobits1 showed you).

You just hard-coded the fields in your program and don't consider the fields in the header.

That said... you can't read the whole header/records as a static record. The binary file contains strings which are #0-terminated. So you can't read a record with a string[255] (which always assumes a string of 255 characters). You need to read the record field by field and when encountering a string only read until #0 is encountered.

Edit: Correction. The strings are not #0 terminated but preceded by the length of the string. But they are still not String[255] which you can read by record. You still need to read the length of the string first and then the number of bytes into that string, no more.
« Last Edit: September 21, 2015, 10:54:13 am by rvk »

eny

  • Hero Member
  • *****
  • Posts: 1646
All posts based on: Win10 (Win64); Lazarus 3_4  (x64) 25-05-2024 (unless specified otherwise...)

GMP_47

  • Jr. Member
  • **
  • Posts: 60
Re: need help with create a file like this
« Reply #7 on: September 21, 2015, 01:48:43 pm »
why number_of_entries?
the sructure was given by them. I translated it the best I could.
Are you sure they didn't just give you the structure as an example. If I look in the binary file I see that the fields are noted in the beginning of the binary file (see attached image). So my guess is you need to first extract the fields (from that header) and after that you read the records (like shobits1 showed you).

You just hard-coded the fields in your program and don't consider the fields in the header.

That said... you can't read the whole header/records as a static record. The binary file contains strings which are #0-terminated. So you can't read a record with a string[255] (which always assumes a string of 255 characters). You need to read the record field by field and when encountering a string only read until #0 is encountered.

Edit: Correction. The strings are not #0 terminated but preceded by the length of the string. But they are still not String[255] which you can read by record. You still need to read the length of the string first and then the number of bytes into that string, no more.

I just opened it with an hex editor. You are right. the 'Header' data is not there, just path, name tel adress email and some of them.
should I just forget about the header?
I got a syntax error anyways. do you know why?

rvk

  • Hero Member
  • *****
  • Posts: 6675
Re: need help with create a file like this
« Reply #8 on: September 21, 2015, 02:35:33 pm »
I just opened it with an hex editor. You are right. the 'Header' data is not there, just path, name tel adress email and some of them.
should I just forget about the header?
I got a syntax error anyways. do you know why?
Those fields (path, name, tel, address and email) ARE the header. So, yeah, I DO think you need to read them.

I'm not sure what your exact assignment is but when I look at the file, I think you should dynamically retrieve the field-definitions from the header and after that read the data.

In that case, if you get a file where there is an extra field added, your program has no trouble reading that too.

So first concentrate on reading that header and translating it to the following to print on screen:

Code: [Select]
Serial: xxx (2 bytes)
Filename: d:/documentos/testing/examen2.dat
Date: 2014/9/20
Number of fields: 4
Field 1: Name
Field 2: Telephone
Field 3: Address
Field 4: EMail
Number of records: 5

After that you can go on and read the actual records. You iterate the number of fields and read a fieldnumber and string into each field (printing it to screen).

Code: [Select]
repeat for every fielddefinition
  read fieldnumber
  read length of string
  read string
  print fieldname (i.e. Name) + ':' + string
until number of records reached.
« Last Edit: September 21, 2015, 02:37:16 pm by rvk »

GMP_47

  • Jr. Member
  • **
  • Posts: 60
Re: need help with create a file like this
« Reply #9 on: September 21, 2015, 04:42:33 pm »
I just opened it with an hex editor. You are right. the 'Header' data is not there, just path, name tel adress email and some of them.
should I just forget about the header?
I got a syntax error anyways. do you know why?
Those fields (path, name, tel, address and email) ARE the header. So, yeah, I DO think you need to read them.

I'm not sure what your exact assignment is but when I look at the file, I think you should dynamically retrieve the field-definitions from the header and after that read the data.

In that case, if you get a file where there is an extra field added, your program has no trouble reading that too.

So first concentrate on reading that header and translating it to the following to print on screen:

Code: [Select]
Serial: xxx (2 bytes)
Filename: d:/documentos/testing/examen2.dat
Date: 2014/9/20
Number of fields: 4
Field 1: Name
Field 2: Telephone
Field 3: Address
Field 4: EMail
Number of records: 5

After that you can go on and read the actual records. You iterate the number of fields and read a fieldnumber and string into each field (printing it to screen).

Code: [Select]
repeat for every fielddefinition
  read fieldnumber
  read length of string
  read string
  print fieldname (i.e. Name) + ':' + string
until number of records reached.

my problem right now is how to establish the Type.
and I meant to say that the serial wasnt on the .dat.

Code: [Select]
uses crt;
Type
  treg = record
         Header = record  //get the error right here
                  serial: smallint ;
                  path : string ;
                  date : smallint ;
                  cant_camp: integer ;
                  end ;

         Data = record
                RegType = record
                          CodCamp : byte [1, 2, 3, 4] ;
                          Descr_camp : string  ;
                          end ;
                RegData = record
                          Ccompletos : integer ;
                          CodCamp : byte [1,2,3,4] ;
                          ContCamp : String ;
                          end ;
         Cant_reg_total: integer ;
         end;

tarch=file of treg;

procedure leer (var arch:tarch; var reg:treg; var bol:boolean);
begin
     if not eof (arch) then begin
        read (arch, reg) ;
        bol:= false ;
     end else begin
        bol:= true ;
     end;

var
arch: tarch ;
reg: treg ;
bol: boolean ;

begin
     assign (arch,'C:\Dev-Pas\EXAMEN2.dat') ;
     reset (arch) ;
     leer (arch, reg, bol) ;
     writeln ;
     writeln ('Nro.de Serie: ', reg.Header.serial) ;
     writeln ;
     writeln ('Full Filename: ',reg.Header.path);
     writeln ;
     writeln ('Fecha Modificacion: ',reg.Header.date);
     writeln ;
     writeln ('Cantidad de Campos Customizados: ',reg.Header.cant_camp);
     writeln ('-------------------') ;

      while not bol do begin
            case reg.Data.RegType.CodCamp of
                                            1 : writeln (reg.Data.RegData.ContCamp) ;      //'Nombre ???
                                            2 : writeln (reg.Data.RegData.ContCamp) ;
                                            3 : writeln (reg.Data.RegData.ContCamp) ;
                                            4 : writeln (reg.Data.RegData.ContCamp) ;
            end;
     close (arch);
     writeln;
     writeln ('emision completa.');
     readkey;
end.
« Last Edit: September 21, 2015, 04:47:12 pm by GMP_47 »

rvk

  • Hero Member
  • *****
  • Posts: 6675
Re: need help with create a file like this
« Reply #10 on: September 21, 2015, 05:41:52 pm »
You still don't get that you can't read the header as complete RECORD !!

A record is a fixed width something. If you have string[255] in that record the string is exactly 255 characters big (+1 for the size). But your header has a variable string inside (path). So you need to read that separately.

I'll show you how to read the header:
Code: [Select]
program read_binary;
uses Classes, SysUtils;

type
  THeader = record
    Serial: Word;
    Filename: String[255];
    Date: Word;
    Fieldnrs: Word;
  end;
  TFields = record
    Fieldnr: Word;
    Fieldname: String[255];
  end;

var
  BinaryStream: TFileStream;
  Header: THeader;
  Fields: array of TFields;
  nrs: Integer;

begin
  BinaryStream := TFileStream.Create('EXAMEN2.dat', fmOpenRead);
  try
    // don't read the whole header at once but piece by peice
    BinaryStream.Read(Header.Serial, 2);
    BinaryStream.Read(Header.Filename[0], 1);
    BinaryStream.Read(Header.Filename[1], ord(Header.Filename[0]));
    BinaryStream.Read(Header.Date, 2);
    BinaryStream.Read(Header.Fieldnrs, 2);

    // the bytes in a word are swapped here !!
    Header.Serial := Swap(Header.Serial);
    Header.Fieldnrs := Swap(Header.Fieldnrs);

    SetLength(Fields, Header.Fieldnrs);
    for nrs := 0 to Header.Fieldnrs - 1 do
    begin
      BinaryStream.Read(Fields[nrs].Fieldnr, 2);
      Fields[nrs].Fieldnr := Swap(Fields[nrs].Fieldnr);
      BinaryStream.Read(Fields[nrs].Fieldname[0], 1);
      BinaryStream.Read(Fields[nrs].Fieldname[1], ord(Fields[nrs].Fieldname[0]));
    end;

    Writeln('Serial: ', Header.Serial);
    Writeln('Filename: ', Header.Filename);
    Writeln('Date: ', Header.Date);
    Writeln('Fieldnrs: ', Header.Fieldnrs);

    for nrs := 0 to Header.Fieldnrs - 1 do
    begin
      Writeln(Fields[nrs].Fieldnr, ' ', Fields[nrs].Fieldname);
    end;

    ReadLn;

    // now go on and read the records...

  finally
    BinaryStream.Free;
  end;
end.

Woops, now I've almost done the whole assignment for you  :o

(But I've done it with a TFileStream so if that's not allowed you still need to convert it to Read() from File  ;D )

GMP_47

  • Jr. Member
  • **
  • Posts: 60
Re: need help with create a file like this
« Reply #11 on: September 22, 2015, 12:28:00 am »
You still don't get that you can't read the header as complete RECORD !!

A record is a fixed width something. If you have string[255] in that record the string is exactly 255 characters big (+1 for the size). But your header has a variable string inside (path). So you need to read that separately.

I'll show you how to read the header:
Code: [Select]
program read_binary;
uses Classes, SysUtils;

type
  THeader = record
    Serial: Word;
    Filename: String[255];
    Date: Word;
    Fieldnrs: Word;
  end;
  TFields = record
    Fieldnr: Word;
    Fieldname: String[255];
  end;

var
  BinaryStream: TFileStream;
  Header: THeader;
  Fields: array of TFields;
  nrs: Integer;

begin
  BinaryStream := TFileStream.Create('EXAMEN2.dat', fmOpenRead);
  try
    // don't read the whole header at once but piece by peice
    BinaryStream.Read(Header.Serial, 2);
    BinaryStream.Read(Header.Filename[0], 1);
    BinaryStream.Read(Header.Filename[1], ord(Header.Filename[0]));
    BinaryStream.Read(Header.Date, 2);
    BinaryStream.Read(Header.Fieldnrs, 2);

    // the bytes in a word are swapped here !!
    Header.Serial := Swap(Header.Serial);
    Header.Fieldnrs := Swap(Header.Fieldnrs);

    SetLength(Fields, Header.Fieldnrs);
    for nrs := 0 to Header.Fieldnrs - 1 do
    begin
      BinaryStream.Read(Fields[nrs].Fieldnr, 2);
      Fields[nrs].Fieldnr := Swap(Fields[nrs].Fieldnr);
      BinaryStream.Read(Fields[nrs].Fieldname[0], 1);
      BinaryStream.Read(Fields[nrs].Fieldname[1], ord(Fields[nrs].Fieldname[0]));
    end;

    Writeln('Serial: ', Header.Serial);
    Writeln('Filename: ', Header.Filename);
    Writeln('Date: ', Header.Date);
    Writeln('Fieldnrs: ', Header.Fieldnrs);

    for nrs := 0 to Header.Fieldnrs - 1 do
    begin
      Writeln(Fields[nrs].Fieldnr, ' ', Fields[nrs].Fieldname);
    end;

    ReadLn;

    // now go on and read the records...

  finally
    BinaryStream.Free;
  end;
end.

Woops, now I've almost done the whole assignment for you  :o

(But I've done it with a TFileStream so if that's not allowed you still need to convert it to Read() from File  ;D )
I'm sorry but I did not understand. I dont know OOP. Cant use Classes.
I checked the .dat with hex editor. Where is the serial?
I'm posting the assignment. Am at a total loss.

derek.john.evans

  • Guest
Re: need help with create a file like this
« Reply #12 on: September 22, 2015, 01:18:49 am »
I'm posting the assignment. Am at a total loss.

Unfortunately, if this is a course work, then you might have to accept a fail mark.
Not a bad thing, because the work will only get harder, and giving you a solution is unfair on the people who worked hard to learn how to do this.

I'm sorry but I did not understand. I dont know OOP. Cant use Classes.

1) If this is course work, then you would have been taught how to load binary files.

2) If this is personal work. Then, take the time to learn a little OOP coding, if only to utilize basic classes like TStream's, TStringList's, TObjectList's etc. Otherwise, you are asking skilled coders to dumb down their code just because you dont want to read a little.

3) If this is actually work for an employer, then, you aren't qualified todo this work.

Take your pick.

GMP_47

  • Jr. Member
  • **
  • Posts: 60
Re: need help with create a file like this
« Reply #13 on: September 22, 2015, 04:01:29 am »
I'm posting the assignment. Am at a total loss.

Unfortunately, if this is a course work, then you might have to accept a fail mark.
Not a bad thing, because the work will only get harder, and giving you a solution is unfair on the people who worked hard to learn how to do this.

I'm sorry but I did not understand. I dont know OOP. Cant use Classes.

1) If this is course work, then you would have been taught how to load binary files.

2) If this is personal work. Then, take the time to learn a little OOP coding, if only to utilize basic classes like TStream's, TStringList's, TObjectList's etc. Otherwise, you are asking skilled coders to dumb down their code just because you dont want to read a little.

3) If this is actually work for an employer, then, you aren't qualified todo this work.

Take your pick.

This is done without classes and I dont have the time right now to learn OOP. But I'll will if I dont have a choice.
why the serial is not on the file?

rvk

  • Hero Member
  • *****
  • Posts: 6675
Re: need help with create a file like this
« Reply #14 on: September 22, 2015, 09:24:41 am »
Why the serial is not on the file?
It IS in the file. The first 2 bytes of your file are $37 and $3D. Since you mentioned a serial could be 43112 it can't be an signed integer which goes from -32,768 to 32,767. So it has to be an unsigned integer/word. So either 15.671 or 14.141 (if hi and low bytes are swapped). So there is your serial. (Since the hi/low of the fieldnrs are swapped I think it's also the latter for the serial)

This is done without classes and I dont have the time right now to learn OOP.
I'm not sure where you see OOP in my example???? I didn't program any OOP. I used TFileStream (which uses OOP) but for using it you don't need OOP yourself at all. So you don't have to learn anything (except how to use TFileStream which is far easier than using assign/reset/read).

But if you really don't want to use TFileStream you can just remove the uses clause at the top and create your read-file. You need to open the file with Reset(f,1) because you need to be able to read byte by byte.

Code: [Select]
var
  BinaryStream: File;

  procedure myRead(var Buffer; Size: Integer);
  begin
    BlockRead(BinaryStream, Buffer, Size);
  end;

begin
  assign(BinaryStream, 'EXAMEN2.dat');
  reset(BinaryStream, 1);
  // don't read the whole header at once but piece by peice
  myRead(Header.Serial, 2);
  myRead(Header.Filename[0], 1);
  myRead(Header.Filename[1], ord(Header.Filename[0]));
  myRead(Header.Date, 2);
  myRead(Header.Fieldnrs, 2);
  //....

The rest is the same as my example with every BinaryStream.Read() replaced by myRead() (and the try/finally removed with a Close() at the end).

You should be able to adjust this all yourself. You should also be able to translate the date-word to a full-date with the description given.

Unfortunately, if this is a course work, then you might have to accept a fail mark.
Not a bad thing, because the work will only get harder, and giving you a solution is unfair on the people who worked hard to learn how to do this.
Giving you a complete solution would not only be unfair on the people who worked hard to learn how to do this but it would also be unfair to you for not giving you the opportunity to learn it. Without learning these basics you end up in a lot more trouble when the assignments get harder.

So with what I showed you so far (and if you understand it) you should be able to read the rest of the file.
« Last Edit: September 22, 2015, 09:30:56 am by rvk »

 

TinyPortal © 2005-2018