Page 1 of 1

Formated Output

Posted: Thu Feb 10, 2011 2:54 pm
by dadda
I am evaluating PureBasic for which I have found good performance (speed two times VB), but some things are nor clear.
I ask information how to make a formated output on a file or a printer.
I find many keywords Write.... for the different variable type, with the limitation of a single variable output for every keywords, a situation which is much less flexible than that in VB where with the Print # statement all can be done in a single line with the option of the default variable spacing with the "," .
In VB the case of a precise formated output with the Format statement it is possible specify how a number must be written ( also in the scientific notetion) and with the TAB() and SPC() funcion define the position in the line.
After the examinatioin of the keyword in PureBasic I think that to have a formated ouput I need to conver the numeric data in a strings and assamble them according the required spacing in a string line and print it; but is not clear how the obtain a scientific output for a real number.
I ask also information for the differnce to have an output on a file or a printer.
I thak very much for every information you can supply to me.

Re: Formated Output

Posted: Thu Feb 10, 2011 3:40 pm
by IdeasVacuum
Using strings is the way in PB. However, 'C' probably has the best way, sprintf. You can use sprintf or similar in PB thanks to some outstanding work by other PB Users:

http://www.purebasic.fr/english/viewtop ... 12&t=41097
http://www.purebasic.fr/english/viewtop ... 12&t=32026

Re: Formated Output

Posted: Thu Feb 10, 2011 6:05 pm
by skywalk
Hi dadda,
I came kicking and screaming to PB from VB6 over a year ago and know how you feel!

True, PB doesn't have the Format$() function.
The file i/o is great, but string concatenation is same or slower than VB6.
There are string memory approaches that crank up the speed if you are inclined.

Here are some routines I use...

Code: Select all

Macro StrDX(x, ndecs, Wd, FillChar=" ", LR=R)   
  ;Debug StrDX(x,2,6,"@")           +"~"   
  ;Debug StrDX(x,3,8,"@",L)         +"~"  
  LR#Set(StrD(x, ndecs), Wd, Fillchar) 
EndMacro

Procedure.s StrDe(X.d, numsd.i=6)
  ;Debug StrDE(1.23456e7, 4)   ; " 12.35e+6" <-- fixed widths
  ;Debug StrDE(-1.23456e7, 4)  ; "-12.35e+6" <-- and Exponent in multiples of 3
  ;Debug StrDE(0, 4)           ; "        0" <-- 0 is simplified, not 0.000e+0 
  ; IN: 
  ;   x = real number to be formatted.
  ;   numsd = integer specifying total number of significant digits.
  ;   "aa.bbbe+3" -> 2 + 3 = 5
  ; RETURN: 
  ;   string of x with numsd significant digits using engineering notation.
  Protected.i Exp, Sgn
  If X > 0    
    Sgn = ' '
  ElseIf X < 0
    Sgn = '-'
    X * -1
  Else              ; Should fill " 000.000e+0", +1 for 'sign', +1 for '.', +3 for 'e+0'
    ProcedureReturn RSet("0", numsd + 5) 
  EndIf
  exp = Round(Log10(X),#PB_Round_Down)
  If exp > 0    
    exp / 3 * 3    
  Else
    exp = (-exp + 3) / 3 * (-3)    
  EndIf
  X = X * Pow(10,-exp)
  If X >= 1000
    X / 1000  
    exp + 3  
  EndIf
  If X >= 100 
    numsd - 3
  ElseIf X >= 10
    numsd - 2
  Else
    numsd - 1
  EndIf
  If numsd < 0
    numsd = 0
  EndIf
  If exp < 0
    ProcedureReturn Chr(sgn) + StrD(x, numsd) + "e" + Str(exp)
  Else
    ProcedureReturn Chr(sgn) + StrD(x, numsd) + "e+" + Str(exp)
  EndIf
EndProcedure

Procedure.i StringToFile(txt$, oFile.s, Append.i=0)
  Protected.i i, of
  If Append   
    of = OpenFile(#PB_Any, oFile)   ; open or create if non-existent
  Else        
    of = CreateFile(#PB_Any, oFile) ; Create new file or delete contents of existing file
  EndIf  
  If of ;And IsFile(of); overkill and slow to use
    FileSeek(of, Lof(of))           ; jump to the end of the file (result of Lof() is used)
    WriteData(of, @txt$, Len(txt$))
    i = Lof(of)
    CloseFile(of)
    ProcedureReturn i
  Else
    MessageRequester("StringToFile","FAIL",#MB_ICONERROR)
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.s FileToStringC(inFile.s)  ; See Trond's simplified/faster FileToString() in later posting.
  ; Purebasic forum: ts-soft
  Protected.i inf, fmt, lng, br, *m
  Protected.s txt
  inf = ReadFile(#PB_Any, inFile)
  If inf ;And IsFile(of); overkill and slow to use
    Fmt = ReadStringFormat(inf)
    lng = Lof(inf)
    If lng
      *m = AllocateMemory(lng)
      If *m
        br = ReadData(inf, *m, lng)
        If br = lng
          Txt = PeekS(*m, lng, fmt)        
        Else
          txt = #NULL$
        EndIf        
        FreeMemory(*m)
      EndIf
    EndIf
    CloseFile(inf)
    ProcedureReturn txt
  Else
    MessageRequester("FileToString",infile + " not ready.",#MB_ICONINFORMATION)
    ProcedureReturn #NULL$
  EndIf  
EndProcedure

; Since PB Native StrD() command does not do exponents :(
Debug StrD(1.23456e7,2)   +"~"    
Debug StrD(1.23456e7,3)   +"~"    
; For Engineering notation...
Debug StrDE(1.23456e7, 4) +"~"  ; " 12.35e+6" <-- fixed widths
Debug StrDE(-1.23456e7, 4)+"~"  ; "-12.35e+6" <-- and Exponent in multiples of 3
Debug StrDE(0, 4)         +"~"  ; "        0" <-- 0 is simplified, not 0.000e+0
; Dropping the exponents, beware truncating your results!
Debug StrDX(1.23456e7,2,6,"@")  +"~"  ; "123456~"
Debug StrDX(1.23456e7,3,8,"@",L)+"~"  ; "12345600~"
Debug StrDX(1.23456e7,3,12,"@",L)+"~" ; "12345600.000~"
Edited per Trond's feedback...

Re: Formated Output

Posted: Thu Feb 10, 2011 8:16 pm
by Trond
Skywalk, are you sure your procedure StrDE() works properly? These outputs don't look right to me:

Code: Select all

Debug StrDE(-54675.5)
Debug StrDE(25)
Here's one that works properly (let's not hope that supposition comes back to haunt me... :lol: )

Code: Select all

Procedure.s StrDe(Num.d, SigDig.i = 6)
  Protected NumSign.i = ' ', ExpSign.s = "+"
  Protected Exponent.i
  
  If Num < 0
    NumSign = '-'
    Num * -1
  EndIf
  
  If Num <> 0
    While Num >= 10
      Num / 10
      Exponent + 1
    Wend
    While Num <= 1
      Num * 10
      Exponent + 1
      ExpSign = "-"
    Wend
  EndIf
  
  ProcedureReturn Chr(NumSign) + StrD(Num, SigDig-1) + "e" + ExpSign + Str(Exponent)
EndProcedure
It's also slightly faster in my test, ignoring that the output differs.

Re: Formated Output

Posted: Thu Feb 10, 2011 8:24 pm
by Trond
I also want to suggest an improvement for your StringToFile(). You use the function IsFile(), which is unnecessary. It's not wrong, but the Is<something>() functions are rather slow and never really needed except for debugging. Testing that the value of the variable "of" is not zero (which you're doing in the code) is sufficient.

Edit: And the same for FileToString.

I would also like to suggest a simpler alternative to FileToString(). It's simpler because it assumes the character encoding of the file is the same as your program (ascii or ucs2 depending on whether you compiled the program with unicode or not). But if it meets your needs it's simpler (and slightly faster in my tests):

Code: Select all

Procedure.s FileToString(inFile.s)
  Protected File.i
  Protected D.s
  File = ReadFile(#PB_Any, inFile)
  If File
    D = Space(Lof(File))
    ReadData(File, @D, Lof(File))
    CloseFile(File)
    ProcedureReturn D
  EndIf
EndProcedure

Re: Formated Output

Posted: Thu Feb 10, 2011 9:59 pm
by skywalk
Trond wrote:Skywalk, are you sure your procedure StrDE() works properly? These outputs don't look right to me:

Code: Select all

Debug StrDE(-54675.5)
Debug StrDE(25)
Hi Trond,
Thanks for verifying these!
Yes, the numbers are the same. Just displayed more efficiently. :wink:
I almost never use Scientific notation and I chastise programmers whenever I see it. :cry:
With Engineering notation, you can "drop" the exponent for an SI unit very simply.
e+3 = "k", e+6 = "M", e-6 = "u", etc.
This imparts more information for the same number sparing my weary brain from having to do the manipulations.

Code: Select all

; Using Engineering notation
Debug StrDE(-54675.5)     ; "-54.6755e+3"
Debug StrDE(25)           ; " 25.0000e+0"
; Using Scientific notation
Debug StrDE(-54675.5)     ; "-5.46755e+4"
Debug StrDE(25)           ; " 2.50000e+1"
Whoa, your file suggestions are super clean.
I didn't understand why isFile() was required if I received a non-zero return from Read or Open or Create File()?

Thanks again, I'll edit the posting...