University of Turku

Convert a file with FreeBasic

Case

In a laboratory there s an old MSDOS system collecting data and saving it to files. I have no idea what components there is and what the system is doing.
The files are binary, meaning that they are not in human readable text format and there is no documentation about the format.
The data can be converted to text with an original program. The problem is that the program can not be used in batch, because it asks the filenames one by one.

The laboratory staff asked me, if I could build some special batch, wich could handle the sittuation. After a quick check I found it easier to build a totally new program to convert the data.

Tools

I have checked and rechecked many programming languages. I have found that good old Basic is very powerfull in cases, where you make some rather simple file conversions. Changing some binary data to some other format is a typical case.
In this case I found that the program got to be compiled so that users have no possibility to mess it by editing the code. This left out for example SmallBasic , whis is wonderful programming system by itself. MS has its own SmallBasic which produces compiled code, but misses very many important commands.

Then I found FreeBasic and there was what I had been looking for !
It's syntax is very near the good old QuickBasic. It has a separated IDE. You can even select between some. I selected Jelly Fish , which somehow felt familiar to me.
There is versions for MSWindows as well as Linux.

How

In the beginning I had to build some simple "Hello World" programs to get accustomed with FreeBasic and JellyFish Then I built a simple program only to read the data files character by character and print them on screen. Every time I found some repeating syntax in the dataflow, I made some pointer in the flow. If ther was a typical structure, for example six characters in a row and then some special character, I added a line feed there.

Slowly I found that in the beginning of the data there is a mass of characters, which are not laboratory data. After that there was two massess of data. I compared these with the tables built by the original converter program and found the number of lines telling how many data points there is and how many columns I should find.

I divided the data so that I go the right number of columns and rows.
Now I could see the data clearly. It was saved column by column. Every data was built of many bytes. I had to find the right format: in the first place it has to be either decimal (523.4) or exponential format (5.234E2). Decimal is easier.

In this case I found that colum 1 was just an index 1 to N. Column 2 and 3 looked like decimal data with no decimal part. Columns 4 and 5 seemed to contain the same data as 2 and 3 but in exponential format. This I found out reading the output of the original converter. So I could leave the columns 4 and 5 out.

Now I had to find the format of colums 2 and 3. First I checked all the format of the index column. There I found the data is in least-significant-byte-first format. So I knew the columns 2 and 3 were similar and built a subfunction for this:

sum = B1+B2*256+B3*256*256+B4*256*256*256 = B1+256*(B2+256*(B3+256*B4))
This made it. I had now columns 1, 2 and 3.

Now I returned to the data mass in the beginning of the file. I was sure it contained information about the file and the measurement gadget. What I needed was the number of data points. I was sure it can not be larger than 65536 which is the largest number, which can be saved in two bytes. I found there two bytes, whose product L=B1+B2*256 was exactly the number of the data points in original file.

Now the mathematics were OK. I built the parts, which saved tha data to a new file, which was compatible with MSExcel. Then I built routines, which can read the parameters from command line, if the program is used in a batch.
Then I built routines, which are used, if the user wants to run the program standalone.
In the end I built a simple help and error handling in the program.

I have a habit to write some simple comments in the program code. It helps me, if I have to change something afterwards. In this case the comments are in native Finnish language, because I had no intention to publish the program.

What now

The program is in active use and nobody has reported any errors. It took me three days at 30% schedule to write the program.

If you have any use of the code, please enjoy yourself.


DOWNLOAD:
B2XLS.EXE
B2XLS.BAS


DIM SHARED versio AS string
versio="130209/2"

' Kari Loikas jatti jonkin mittalaitteen tuottaman tiedoston kasiteltavaksi.
' ratkaistu raalla voimalla (ja koristeltu;).            
' Ohjelma kirjoitettu FreeBasic:llä 
' Tama versio tuottaa txt-muotoisen tiedoston, jossa sarakkeet 1 ja 2

DIM i AS integer    'for/next laskuri
DIM j AS integer    'for/next laskuri 
DIM temp AS string    
DIM mrivi AS string
DIM rivi AS integer
DIM sar1 AS double  'ensimmaisen sarakkeen lukema siirrettaessa tiedostosta toiseen 
DIM m1 AS string
DIM m2 AS string 
DIM m3 AS string 
DIM SHARED m4 AS string       
DIM SHARED MM4 AS double
DIM MM3 AS double
DIM MM2 AS double
DIM MM1 AS double
DIM sum AS double
DIM lkm AS double        
DIM lkm2 AS double                  'rivien lkm
DIM ohi AS double                   'ohitettujen rivien lkm
DIM merkkeja AS double              'merkkien lkm tiedostossa
DIM argc AS Integer, argv(5) AS String 'komentojonoparametrit
DIM sorsa AS string
DIM tulos AS string

'e:\Loikas_test\AA01.400 e:\loikas_test\AA01.txt

DECLARE SUB help()  
DECLARE SUB kaiva(j AS integer)

temp=ENVIRON("temp")                'jarjestelman temp -hakemisto.   

' Haetaan komentojonoparametrit      
' argc vastaa itse ohjelman nimea
argc = 1        

DO
     argv(argc) = COMMAND( argc )
     IF( LEN( argv(argc) ) = 0 ) THEN
         EXIT DO
     END IF 
     IF INSTR(argv(argc),"?")>0 THEN
        'Komentorivilla '?', tulostetaan HELP
         help()
     END IF
     argc += 1    
LOOP          
        
IF argc>4 THEN 
    'enintaan kolme parametria, virhetilassa tulostetaan help
    PRINT "------------------------"
    PRINT "------------------------"
    PRINT "!! Liikaa parametreja !!" ;argc,argv(argc)
    PRINT "------------------------"
    PRINT "------------------------"
    help()
ENDIF

IF argc=1 THEN    
    'ei komentoriviparametreja  
    PRINT "'#' lähdetiedoston nimen alussa lisää rivinumeroinnin tulostiedostoon": PRINT
    INPUT "Lahdetiedosto";sorsa
    INPUT"Tulostiedosto";tulos    
    'print "TULOS=>";tulos;"<"
    IF sorsa="" THEN 
        PRINT"----------------------------"
        help()
    ENDIF                          
    IF INSTR(sorsa,"#") THEN
        sorsa=LTRIM(LTRIM(sorsa,"#")," ")
        argv(1)="#"        
    ENDIF
ELSEIF argv(1)<>"#" THEN     
    ' Ei risuaitaa komentorivin alussa   
    sorsa=argv(1)
    tulos=argv(2)
ELSEIF argv(1)="#" THEN   
    'aitamerkki ensimmaisena parametrina
    sorsa=argv(2)
    tulos=argv(3)  
    IF argv(2)="" THEN   
        PRINT"-------------------------------------"
        PRINT"-------------------------------------"
        PRINT"--!!! PUUTTEELLISET LAHTOTIEDOT !!!--"   
        PRINT"-------------------------------------"        
        PRINT"-------------------------------------"       
        help()
    ENDIF
ENDIF                 

OPEN sorsa FOR BINARY  AS #1    
' Toivottavasti tallaista tiedostoa ei ole olemassa:
temp=temp+"\q1w2e3r4.tmp"
OPEN temp FOR OUTPUT AS #2

merkkeja=LOF(1)                    
'PRINT"Tiedostossa merkkeja:";LOF(1)

FOR i=1 TO 60  
    'luetaan alusta pois 60 merkkiä
    m1=INPUT$(1,1)
    MM1=ASC(m1)
NEXT                                           
ohi=60

'PRINT:PRINT"Alkutyhjat jatetty pois (60 kpl)" 
' Kaivetaan tiedoston pituus.
' Jos rivien lukumaara on yli 256, tassa _saattaa_ tulla virhe, jolloin kaytetaan 
' tassa alla olevaa ensimmaista laskutapaa. 
m1=INPUT$(1,1)
MM1=ASC(m1)   
m2=INPUT$(1,1)  
MM2=ASC(m2)  
lkm=MM1+MM2*256        
m3=INPUT$(1,1)
MM3=ASC(m3)                      
'Jos virheellinen rivien lkm, komentoi alla oleva rivi:
lkm=MM2*256 + MM3
ohi=ohi+3                  

IF lkm=0 THEN 
    PRINT"-----------------------------------------------------"
    PRINT"-----------------------------------------------------"    
    PRINT"SYOTTOTIEDOSTOA EI LOYDY TAI SEN SISALTO VIRHEELLINEN"
    PRINT"-----------------------------------------------------"
    PRINT"-----------------------------------------------------"
    help()                
ENDIF    

/' Tarkistettu, rutiini toimii
IF argc=1 THEN 
    PRINT"------"
    PRINT "( Lukumaara_2 = 256*asc(m2) + asc(m3) ) = ";lkm
    PRINT"Jos rivien lukumaara on virheellinen, pitaa ohjelmakoodia muuttaa"
    PRINT "Ota yhteytta: ptmusta@utu.fi"
    PRINT"-------"
    PRINT "Paina jotain nappainta"
    DO:LOOP UNTIL inkey>""
ENDIF     
'/
     
FOR i=64 TO 170  
    m1=INPUT$(1,1)
    MM1=ASC(m1)
    ' Print mm1;
NEXT            
ohi=ohi+106
FOR i=171 TO 194
    m1=INPUT$(1,1)
    MM1=ASC(m1)
    ' Print i, m1;mm1
NEXT       
ohi=ohi+23                
             
'----------------------------------- OLEELLINEN OSA TIEDOSTOSTA ALKAA-----------------------------------             
FOR i=1 TO lkm 
    'Luetaan ensimmainen sarake ja viedaan temp-tiedostoon
    sum=0          
    m1=INPUT$(1,#1):MM1=ASC(m1):sum=sum+MM1 
    m2=INPUT$(1,#1):MM2=ASC(m2):sum=sum+MM2*256      
    m3=INPUT$(1,#1):MM3=ASC(m3):sum=sum+MM3*256*256 
    m4=INPUT$(1,#1):MM4=ASC(m4):sum=sum+MM4*256*256*256            
PRINT #2,sum
NEXT            
CLOSE 2
    
ohi=ohi+lkm
' Avataan ensimmaisen sarakkeen sisaltava temptiedosto uudestaan luettavaksi 
' Ja lopullinen tiedosto kirjoitettavaksi                               
OPEN temp FOR INPUT AS #2    

IF tulos="" THEN 
    'tulostiedoston nimi puuttuu, annetaan nimeksi 
    'lähtötiedoston nimi ja jatkeeksi 'txt'
    tulos="txt"
ENDIF
IF (LEN(tulos)<=3)AND(tulos<>"$") THEN
    'komentorivillä annettu vain nimen jatke tulostiedoston nimen kohdalla
    tulos=LEFT(sorsa,INSTR(sorsa,"."))+tulos
ENDIF
                               
IF ((tulos<>"") AND (tulos<>"$")) THEN
    'tulostus tiedostoon vain, jos tiedosto on nimetty
    OPEN tulos FOR OUTPUT AS #3                      
ENDIF

rivi=0:mrivi=""
FOR i=1 TO lkm 
    'haetaan ensimmaisen sarakkeen rivi
    INPUT #2,sar1                      
    'lasketaan toisen sarakkeen arvo
    sum=0  
    m1=INPUT$(1,#1):MM1=ASC(m1):sum=sum+MM1 
    m2=INPUT$(1,#1):MM2=ASC(m2):sum=sum+MM2*256                                                                
    m3=INPUT$(1,#1):MM3=ASC(m3):sum=sum+MM3*256*256
    m4=INPUT$(1,#1):MM4=ASC(m4):sum=sum+MM4*256*256*256 
    IF argv(1)="#" THEN               
        'rivinro tiedostoon vain, jos on pyydetty
        rivi=rivi+1
        mrivi=STR(rivi)+","  
    ENDIF  
   
    IF tulos="$" THEN 
        'tulostuu ruudulle, kun tulostiedoston nimi=$     
        PRINT mrivi;sar1;sum
    ELSE                                             
        'tulostiedoston nimi annettu
        PRINT #3,mrivi;sar1;",";sum
    ENDIF

NEXT     
CLOSE 2,3
ohi=ohi+lkm            
          
PRINT"KASITELTY: ";sorsa

IF argc=1 THEN                    
    'tyhjä syöttörivi
    PRINT" Paina jotain nappainta"
    DO:LOOP UNTIL INKEY$>""
    PRINT
    PRINT"-------------------"
    PRINT
ENDIF

'----------------------------------- OLEELLINEN OSA TIEDOSTOSTA LOPPUU-----------------------------------    

'-------------------------------------------
' Jatetaan loput kasittelematta. Tasta eteenpain ainoastaan kaiva() ja help() koodin lopussa
'-------------------------------------------
END
PRINT "Ohitettu sarake 2. Paina nappainta"
DO:LOOP UNTIL inkey>""

kaiva(lkm)                     
ohi=ohi+lkm
PRINT "Ohitettu sarake 3. Paina nappainta"       
DO:LOOP UNTIL INKEY$>""                          

kaiva(lkm)
ohi=ohi+lkm
PRINT"Ohitettu sarake 4. Paina nappainta"       
DO:LOOP UNTIL INKEY$>""

kaiva(lkm)
ohi=ohi+lkm
PRINT"Ohitettu sarake 5. Paina nappainta"       
PRINT "Taytemerkkeja jaljella ";merkkeja-ohi   
DO:LOOP UNTIL INKEY$>""                         

kaiva(merkkeja-ohi)
PRINT"Ohi on. Paina nappainta"       
DO:LOOP UNTIL INKEY$>""
                                                                                
CLOSE 1
'--------------------------------------------------------------------------------------
SUB kaiva(BYVAL j AS integer) 
DIM i AS integer    'for/next laskuri
    FOR i=1 TO j
        m4=INPUT$(1,#1):MM4=ASC(m4):PRINT i,m4;
        m4=INPUT$(1,#1):MM4=ASC(m4):PRINT m4;
        m4=INPUT$(1,#1):MM4=ASC(m4):PRINT m4;
        m4=INPUT$(1,#1):MM4=ASC(m4):PRINT m4                                               
    NEXT     
END SUB
'---------------------------------------------------------------------------------------
SUB help()   
COLOR(14,0)  
    PRINT "B2XLS [#|?] sourcefile [destinationfile|jat|$]";    
COLOR(7,0)
    PRINT"    (jarjestys on tarkea)"    
    PRINT "Ohjelma avaa annetun tiedoston ja poimii sielta kaksi ensimmaista saraketta."
    PRINT "Sarakkeiden sisalto talletetaan uuteen tiedostoon."
    PRINT "sarakkeiden erottimena on pilkku"
    PRINT "'#' - tiedoston ensimmaiseksi sarakkeeksi rivin numero pilkulla erotettuna"   
    PRINT "'?' - tulostaa taman ohjeen"
    PRINT "Jos tulostiedostoa ei anneta, tulostustiedoston nimi on lähdetiedoston nimi, jatkeena 'txt'" 
    PRINT "Jos tulostiedoston tilalla annetaan tiedoston jatke 'jat', käytetään tätä nimen jatkeena"
    PRINT "'$' tulostiedoston nimenä tulostaa ruudulle"                                           
    PRINT "Tama ohjelma on rakennettu olemassaolevan lahde- ja tulostiedoston pohjalta"
    PRINT "siita on jatetty pois kolmen viimeisen sarakkeen tulostus, koska nama sisaltavat"
    PRINT "ainoastaan seuraavat tiedot:"
    PRINT "- rivin jarjestysnumero"
    print "- sarake 1. exponenttimuodossa"
    PRINT "- sarake 2. exponenttimuodossa"
    PRINT "Versio: ";versio; ", PTMusta@utu.fi"     
    
    DO:LOOP UNTIL inkey>""
    END
END SUB
    
PTMUSTA at UTU.FI