Can't thank you enough for assisting with this. I'll probably make a few more changes with e.g. the line number column and few other tweaks. But for now, here's my version.
Code: Select all
#include "hmg.ch"
#Include "Fileio.CH"
#include "hbthread.ch"
#DEFINE FS_BEGINNING 0 // From Beginning of file
#DEFINE FS_RELATIVE 1 // From Current Pointer
#DEFINE FS_END 2 // From End of File
Function Main
Local cFileToOpen := ""
Local cDelimiter := CHR ( 9 )
Local oGridFile
Local pSeekThread, pSeekMethodThread
DEFINE WINDOW Form_1 ;
AT 0,0 ;
WIDTH 1700 ;
HEIGHT 1000 ;
TITLE 'Tab-delimited text file viewer' ;
MAIN ;
ON INIT DoNothing()
DEFINE MAIN MENU
DEFINE POPUP 'File'
MENUITEM 'Select Tab Delimited file)' ACTION ( cFileToOpen := Getfile ( { {'TSV Files','*.tsv'} } , 'Open TSV File' , GetCurrentFolder() , .f. , .f. ), oGridFile := OpenFile( cFileToOpen, cDelimiter, 1 ) )
SEPARATOR
ITEM 'Exit' ACTION ThisWindow.Release
END POPUP
END MENU
@10, 10 TEXTBOX tb_goto WIDTH 100 VALUE 0 NUMERIC
@10, 110 BUTTON bGoto CAPTION "Go to line" ACTION Form_1.Grid_1.Value := Form_1.tb_goto.Value
@10, 315 TEXTBOX tb_Seek WIDTH 200 VALUE ""
@10, 520 BUTTON bSeekMethod CAPTION "Search" ACTION MT_SeekLine( oGridFile )
@10, 725 TEXTBOX tb_record WIDTH 100 VALUE 1 NUMERIC
@10, 830 BUTTON bRecord CAPTION "Read Line" ACTION ( oGridFile:GoTo( Form_1.tb_record.Value + 1 /* "+ 1" first line is the header */ ), MsgBox( oGridFile:ReadLine() ) )
Form_1.tb_goto.Visible := .F.
Form_1.bGoto.Visible := .F.
Form_1.tb_record.Visible := .F.
Form_1.bRecord.Visible := .F.
Form_1.tb_Seek.Visible := .F.
Form_1.bSeekMethod.Visible := .F.
DEFINE STATUSBAR
STATUSITEM "" WIDTH Form_1.Width / 2
STATUSITEM "" WIDTH Form_1.Width / 2
END STATUSBAR
ShowProgressBar( 1, , , (Form_1.Width / 2) + 5 , Form_1.Width / 2 - 30 ) //init progress bar
ShowProgressBar( 0 ) //hide progress bar
END WINDOW
ACTIVATE WINDOW Form_1
Return Nil
***************************************************
***************************************************
Function QueryFile( oFile, cDelimiter )
Local nRecord := This.QueryRowIndex
Local nCol := This.QueryColIndex
Local nListRows := LISTVIEWGETCOUNTPERPAGE ( Form_1.Grid_1.Handle ) //Number of visible lines in the grid
Local nItemCount := Form_1.Grid_1.ItemCount
Local nIncrementalRead := 1000 //nListRows * 2
Local aMemAlloc := {}
Static aFields := {}
// xVar := This
IF oFile == Nil
Return Nil
ENDIF
IF oFile:CurrentLine() <> nRecord + 1 /* "+ 1" first line is the header */ .OR. Len ( aFields ) == 0 //Prevents the current row from being reread when query to display the next column (speeding up)
oFile:GoTo ( nRecord + 1 /* "+ 1" first line is the header */ )
aFields := hb_ATokens ( oFile:ReadLine(), cDelimiter )
AAdd( aFields, "" )
AAdd( aFields, "" )
AIns( aFields, 1 )
aFields[ 1 ] := This.QueryRowIndex
ENDIF
This.QueryData := IF ( Len ( aFields ) < nCol , "", aFields [ nCol ] )
Return Nil
**************************************************************
Function OpenFile( cFileToOpen, cDelimiter, nMode )
Local aColumns := {}
Local aHeaders := {}
Local aWidths := {}
Local i
Local oFile
Local pMutexCount, nListRows
Default nMode := 0
IF File ( cFileToOpen )
oFile := vfFileRead():New( cFileToOpen, , , )
oFile:Open()
IF oFile:Error()
MsgStop ("Error " + Str( oFile:ErrorNo() ), "Error" )
RETURN Nil
ENDIF
IF IsControlDefined ( Grid_1, Form_1 )
Form_1.Grid_1.Release
ENDIF
//Preparing columns (based on the first row from the file)
aColumns := hb_ATokens ( oFile:ReadLine(), cDelimiter )
AAdd( aColumns, "Null" )
AIns( aColumns, 1 )
aColumns[ 1 ] := "LineNumber"
For i := 1 TO Len( aColumns )
AAdd( aHeaders, aColumns [i] )
AAdd( aWidths, Max(20, Len( aColumns [ i ] ) * 19 ) )
Next i
//Calculate the number of rows before displaying the Grid
WAIT WINDOW "Counting the number of records ..." NOWAIT
ShowProgressBar( 2 ) //show progress bar
Form_1.StatusBar.Item(1) := "Counting the number of records ... This may take some time .... "
//code block executed while the CountLines() method is running
oFile:exGauge := { | nPos, nLastPos, nLines, cState | ( Form_1.StatusBar.Item(1) := "Counting the number of records: " + AllTrim ( Str ( nLines ) ) + "... This may take some time ... " + cState, Form_1.PBar.Value := nPos / nLastPos * 100, doEvents() ) }
oFile:CountLines()
Form_1.StatusBar.Item(1) := ""
ShowProgressBar( 0 ) //hide progress bar
WAIT CLEAR
@ 40,10 GRID Grid_1 ;
OF Form_1 ;
WIDTH 1600 ;
HEIGHT 800 ;
VALUE 1 ;
HEADERS aHeaders ;
WIDTHS aWidths;
VIRTUAL ;
ITEMCOUNT 0 ;
ON QUERYDATA QueryFile( oFile, cDelimiter )
Form_1.Grid_1.PaintDoubleBuffer := .T.
Form_1.Grid_1.ItemCount := oFile:nLastLine - 1 /* "- 1" first line is the header */
Form_1.tb_goto.Visible := .T.
Form_1.bGoto.Visible := .T.
Form_1.tb_record.Visible := .T.
Form_1.bRecord.Visible := .T.
Form_1.tb_Seek.Visible := .T.
Form_1.bSeekMethod.Visible := .T.
Form_1.Grid_1.Value := 1
Form_1.Grid_1.Setfocus
ON KEY CONTROL+F OF Form_1 ACTION Eval ( _GetControlAction ( 'bSeekMethod' , 'Form_1' ) )
ON KEY CONTROL+G OF Form_1 ACTION Eval ( _GetControlAction ( 'bGoto' , 'Form_1' ) )
ON KEY CONTROL+R OF Form_1 ACTION Eval ( _GetControlAction ( 'bRecord' , 'Form_1' ) )
ENDIF
RETURN oFile
Function Refr_Mem_Stat()
DO WHILE .T.
FT_Sleep( 500 )
ENDDO
RETURN Nil
*********************************************
*********************************************
FUNCTION MessageRecNo( nValidRec, cRec )
IF Val( cRec ) == nValidRec
MsgInfo ( cRec ,"The record number is correct." )
ELSE
MsgStop ( cRec ,"The record number is invalid!, schould be " +AllTrim( Str( nValidRec ) ) )
ENDIF
RETURN Nil
***************************************************************************
Function ShowProgressBar( nMode, nMin, nMax, nCol, nLenght )
Default nMode := 1 //1 = init, 2 = set/show, 3 = close, 0/other = hide
Default nMin:=1, nMax:=100
Default nCol:=20, nLenght:=740
DO CASE
Case nMode = 1
DEFINE PROGRESSBAR PBar
PARENT Form_1
ROW 10
COL nCol
WIDTH nLenght
HEIGHT 12
RANGEMIN nMin
RANGEMAX nMax
VALUE nMin
TOOLTIP ""
HELPID Nil
VISIBLE .F.
SMOOTH .T.
VERTICAL .F.
BACKCOLOR Nil
FORECOLOR Nil
END PROGRESSBAR
/* put the progress bar in the status bar */
SETPARENT(Form_1.PBar.Handle, Form_1.STATUSBAR.Handle)
Case nMode = 3
Form_1.PBar.Release
Case nMode = 2
Form_1.PBar.RangeMin := nMin
Form_1.PBar.RangeMax := nMax
Form_1.PBar.Value := nMin
Form_1.PBar.Visible := .T.
DO EVENTS
Other
Form_1.PBar.Visible := .F.
DO EVENTS
ENDCASE
RETURN Nil
*******************************************************************
*******************************************************************
Function MT_SeekLine( oFile )
Local lFound
Local ValueToSeek_s
Local TopOfGrid_n
IF !IsControlDefined ( Grid_1, Form_1 )
RETURN Nil
ENDIF
Form_1.StatusBar.Item(1) := "Seeking ... This may take some time .... "
ShowProgressBar( 2 ) //show progress bar
ValueToSeek_s := AllTrim( Form_1.tb_Seek.Value )
TopOfGrid_n := Form_1.Grid_1.Value
lFound := oFile:SeekLine( ValueToSeek_s, TopOfGrid_n + 1 /* "+ 1" first line is the header */ , .F. , { | nPos, nLastPos | Form_1.PBar.Value := nPos / nLastPos * 100 } )
Form_1.StatusBar.Item(1) := ""
ShowProgressBar( 0 ) //hide progress bar
IF !lFound
MsgStop ("Not found " + AllTRim( Form_1.tb_Seek.Value ) )
ELSE
Form_1.Grid_1.Value := oFile:CurrentLine() - 1 /* "- 1" first line is the header */
ENDIF
Form_1.Grid_1.Setfocus
RETURN Nil
*******************************************************************
*******************************************************************
* vfFileRead Class *
*******************************************************************
#include "hbclass.ch"
#include "fileio.ch"
#define vf_DEF_READ_SIZE 4096
#define vf_DELIMITER hb_eol()
#define vf_REC_MAP_STEP 100000
CREATE CLASS vfFileRead
VAR cFile
VAR pHandle
VAR nError
VAR cDelim
VAR nReadSize
VAR lEOF
VAR lBOF
VAR nCurrLine
VAR nLastLine
VAR exGauge
VAR aRecordsMap INIT {} //PROTECTED
VAR nRecordsMapStep PROTECTED
METHOD New( cFile, nSize, cDelimiter, nRecMapStep ) // Create a new object (file name, buffer size , delimiter, map record indexed step)
METHOD Open( nMode ) // Open the file ( nMode )
METHOD Close() // Close the file
METHOD ReadLine() // Read line of current record (when opened, current record is first)
METHOD GoTop() // Go to first record
METHOD GoBottom() // Go to last record
METHOD GoTo( nLine ) // Go to <nLine> record
METHOD Skip( nSkip ) // Skip by <nSkip> records
METHOD IsEOF() // Returns .T. if the current record is the last
METHOD IsBOF() // Returns .T. if the current record is the first
METHOD Error() // Returns .T. an error occurred
METHOD ErrorNo() // Returns the error number
METHOD GetPointer() // Reads the current position of the file pointer
METHOD SetPointer( nPointer ) // Sets the file pointer to the indicated position
METHOD GetLastLinePointer() // Reads the file pointer position for the last record
METHOD CurrentLine() // Reads the current record
METHOD CountLines( xGauge ) // Calculates the number of lines (records), as a parameter you can pass a block of code executed during the counting operation
METHOD SeekLine( cSeekString, nFromRecord, lCaseSensitive, xGauge ) //Seek <cSeekString> in line, begin from <nFromRecord> and if <lCaseSensitive> (default is true) //Rerturn true or false, set ::CurrentLine() to matched line
METHOD GetRecordsMap() // Gets a map of records
METHOD PutRecordsMap( aMap ) // Puts a map of records
END CLASS
*******************************************************************
METHOD New( cFile, nSize, cDelimiter, nRecMapStep ) CLASS vfFileRead
Default nRecMapStep := vf_REC_MAP_STEP
nRecMapStep := Max ( nRecMapStep, 10000 )
IF nSize == NIL .OR. nSize < 1
nSize := vf_DEF_READ_SIZE
ENDIF
::cFile := cFile
::pHandle := NIL
::nError := 0
::nReadSize := nSize
::cDelim := cDelimiter
::lEOF := .F.
::lBOF := .F.
::nCurrLine := 0
::nLastLine := NIL
::aRecordsMap:={}
::nRecordsMapStep := nRecMapStep
RETURN Self
*******************************************************************
METHOD Open( nMode ) CLASS vfFileRead
Local cLine
IF ::pHandle == NIL
IF nMode == NIL
nMode := FO_READ // Default to read-only mode
ENDIF
::pHandle := hb_vfOpen( ::cFile, nMode )
::aRecordsMap:={}
IF ::pHandle == NIL
::nError := FError()
::lBOF := .F.
::nCurrLine := 0
ELSE
::nError := 0
::lBOF := .T.
::nCurrLine := 1
//delimiter detection
IF ::cDelim == NIL
::cDelim := vf_DELIMITER
cLine := hb_vfReadLen( ::pHandle, 64*1024 /* ::nReadSize */ )
::GoTop()
DO CASE
CASE hb_AT( CHR (13) + CRLF, cLine ) > 0 //CRCRLF
::cDelim := CHR (13) + CRLF
CASE hb_AT( CRLF, cLine ) > 0 //CRLF
::cDelim := CRLF
CASE hb_AT( CHR (13), cLine ) > 0 //CR
::cDelim := CHR (13)
CASE hb_AT( CHR (10), cLine ) > 0 //LF
::cDelim := CHR (10)
ENDCASE
ENDIF
ENDIF
ELSE
// The file is already open, so rewind to the beginning.
IF !::GoTop()
::lBOF := .F.
::nCurrLine := 0
::nError := FError()
ELSE
::lBOF := .T.
::nCurrLine := 1
ENDIF
ENDIF
RETURN Self
*******************************************************************
METHOD Close() CLASS vfFileRead
IF .NOT. ::pHandle == NIL
hb_vfClose( ::pHandle )
::pHandle := NIL // The file is no longer open
ENDIF
::nError := FError()
::lEOF := .F.
::lBOF := .F.
::nCurrLine := 0
::nLastLine := NIL
RETURN Self
*******************************************************************
METHOD ReadLine() CLASS vfFileRead
Local cLine := ""
Local nCurrentPos := ::GetPointer()
DO WHILE !hb_vfEof( ::pHandle )
cLine += hb_vfReadLen( ::pHandle, ::nReadSize )
IF hb_AT( ::cDelim, cLine ) > 0
cLine := Left ( cLine, hb_AT( ::cDelim, cLine ) - 1 )
EXIT
ENDIF
ENDDO
::SetPointer ( nCurrentPos )
::nError := FError()
RETURN cLine
*******************************************************************
METHOD GoTop() CLASS vfFileRead
::lEOF := .F.
::lBOT := .T.
::nCurrLine := 1
RETURN hb_vfSeek( ::pHandle, 0, FS_BEGINNING ) == 0
*******************************************************************
METHOD GoBottom() CLASS vfFileRead
hb_vfSeek( ::pHandle, 0, FS_END )
::Skip( -1 )
::lEOF := .T.
::lBOT := .F.
::nCurrLine := ::nLastLine
::nError := FError()
RETURN FError() == 0
*******************************************************************
METHOD GoTo( nLine ) CLASS vfFileRead
Local nToSkip ,nIntStep, nPos
DEFAULT nLine := ::nCurrLine
IF !hb_isNumeric (nLine)
RETURN Self
ENDIF
IF ABS ( nLine - ::nCurrLine ) >= ::nRecordsMapStep //only if we skip by indexed steps
nIntStep := ( INT( nLine / ::nRecordsMapStep ) + IF ( nLine % ::nRecordsMapStep < (::nRecordsMapStep / 2), 0, 1 ) ) * ::nRecordsMapStep
DO WHILE nIntStep >= 0
nPos := hb_AScan( ::aRecordsMap, { |x| Bin2ULL ( ULLUnShrink ( x [ 1 ] ) ) == nIntStep } )
IF nPos > 0
::nCurrLine := Bin2ULL ( ULLUnShrink ( ::aRecordsMap [ nPos ] [ 1 ] ) )
::SetPointer( Bin2ULL ( ULLUnShrink ( ::aRecordsMap [ nPos ] [ 2 ] ) ) )
EXIT
ENDIF
//Try with previous indexed step
nIntStep -= ::nRecordsMapStep
ENDDO
ENDIF
nToSkip := nLine - ::nCurrLine
DO CASE
CASE hb_isNil( ::nCurrLine ) //We do not know the current line number, so start at the beginning of the file
::GoTop()
::Skip ( nLine - 1 )
CASE nLine <= 1 //Line number less than 1, so start at the beginning of the file
::GoTop()
CASE hb_isNumeric( ::nLastLine ) .AND. nLine >= ::nLastLine //Go beyond the known last line number, so go to the end.
::GoBottom()
CASE hb_isNumeric( ::nLastLine ) .AND. ::nLastLine - nLine < ABS ( nToSkip ) //We know the number of the last record, the nToSkip is greater than the difference between the last and the target record - so skip backward from the end (less skips)
::GoBottom()
::Skip( 0 - (::nLastLine - nLine ) )
CASE nLine < 0 - nToSkip //The target line is closer to the start than the nToSkip, so start at the beginning of the file.
::GoTop()
::Skip ( nLine - 1 )
OTHER
::Skip ( nToSkip )
ENDCASE
::nError := FError()
RETURN Self
*******************************************************************
METHOD Skip ( nSkip ) CLASS vfFileRead
Local cLine := ""
Local lRun := .T.
Local nSkips := 0
Local nBuffSize := 64 * 1024 //::nReadSize //A larger buffer speeds up the process
Local nLenDelim := Len (::cDelim)
Local nStart, nEnd, nBackOf, nPos, nPointer
Default nSkip := 1
IF nSkip == 0
RETURN Self
ENDIF
::lEOF := .F.
::lBOF := .F.
DO WHILE lRun
IF nSkip > 0
IF hb_vfEof( ::pHandle )
::Skip ( -1 )
::nLastLine := ::nCurrLine
::lEOF := .T.
EXIT
ENDIF
nStart := 1
nPointer := ::GetPointer()
cLine := hb_vfReadLen( ::pHandle, nBuffSize )
DO WHILE Right (cLine, 1) $ ::cDelim .AND. !hb_vfEof( ::pHandle ) //Avoid splitting of end-of-line marks in the buffer.
cLine += hb_vfReadLen( ::pHandle, nLenDelim )
ENDDO
DO WHILE ( nPos := hb_AT( ::cDelim, cLine, nStart ) ) > 0
nSkips ++
IF hb_isNumeric( ::nCurrLine )
::nCurrLine ++
IF ::nCurrLine % ::nRecordsMapStep == 0 .AND. hb_AScan( ::aRecordsMap, { |x| Bin2ULL ( ULLUnShrink ( x [ 1 ] ) ) == ::nCurrLine } ) == 0
AAdd( ::aRecordsMap, { ULLShrink( ULL2Bin( ::nCurrLine ) ), ULLShrink( ULL2Bin( nPointer + nPos + nLenDelim - 1 ) ) } )
ENDIF
ENDIF
nStart := nPos + nLenDelim
IF nSkips == nSkip
hb_vfSeek( ::pHandle, 0 - ( Len ( cLine ) - nStart + 1), FS_RELATIVE )
lRun := .F.
IF hb_vfEof( ::pHandle )
::Skip ( -1 )
::nLastLine := ::nCurrLine
::lEOF := .T.
ENDIF
EXIT
ENDIF
ENDDO
ELSE // -skip
IF ::GetPointer() == 0 //top of file
::lBOF := .T.
::nCurrLine := 1
EXIT
ENDIF
cLine := ""
DO WHILE ( nPos := hb_AT( ::cDelim, cLine ) ) == 0 .AND. hb_vfSeek( ::pHandle, 0, 1 ) <> 0 //Avoid splitting of end-of-line marks in the buffer.
nBackOf := Min( nBuffSize, ::GetPointer() )
hb_vfSeek( ::pHandle, 0 - nBackOf, FS_RELATIVE )
cLine := hb_vfReadLen( ::pHandle, nBackOf ) + cLine
hb_vfSeek( ::pHandle, 0 - nBackOf, FS_RELATIVE )
ENDDO
IF nPos == 0
nPos := 1
ENDIF
nStart := nPos
cLine := SubStr ( cLine, nStart )
hb_vfSeek( ::pHandle, nStart - 1, FS_RELATIVE )
nPointer := ::GetPointer()
nEnd := Len( cLine )
IF Right( cLine, nLenDelim) == ::cDelim
nEnd -= nLenDelim
ENDIF
DO WHILE (nPos := hb_RAT( ::cDelim, cLine, 1, nEnd ) ) > 0
nSkips --
IF hb_isNumeric( ::nCurrLine )
::nCurrLine --
IF ::nCurrLine % ::nRecordsMapStep == 0 .AND. hb_AScan( ::aRecordsMap, { |x| Bin2ULL ( ULLUnShrink ( x [ 1 ] ) ) == ::nCurrLine } ) == 0
AAdd( ::aRecordsMap, { ULLShrink( ULL2Bin( ::nCurrLine ) ), ULLShrink( ULL2Bin( nPointer + nPos + nLenDelim -1 ) ) } )
ENDIF
ENDIF
nEnd := nPos - 1 //nLenDelim
IF nSkips == nSkip
hb_vfSeek( ::pHandle, 0 + nPos + nLenDelim - 1 , FS_RELATIVE )
lRun := .F.
EXIT
ENDIF
ENDDO
ENDIF
ENDDO
cLine := ''
::nError := FError()
RETURN Self
*******************************************************************
METHOD IsEOF() CLASS vfFileRead
IF !::lEOF .AND. hb_IsNumeric ( ::nCurrLine ) .AND. hb_IsNumeric ( ::nLastLine ) .AND. ::nCurrLine == ::nLastLine
::lEOF := .T.
ENDIF
IF !::lEOF .AND. ::GetPointer() == ::GetLastLinePointer()
::lEOF := .T.
ENDIF
RETURN hb_vfEof( ::pHandle ) .OR. ::lEOF
*******************************************************************
METHOD IsBOF() CLASS vfFileRead
RETURN ::GetPointer() == 0 .OR. ::lBOF
*******************************************************************
METHOD Error() CLASS vfFileRead
RETURN FError() != 0
*******************************************************************
METHOD ErrorNo() CLASS vfFileRead
RETURN ::nError
*******************************************************************
METHOD GetPointer() CLASS vfFileRead
RETURN hb_vfSeek( ::pHandle, 0, FS_RELATIVE )
*******************************************************************
METHOD SetPointer( nPointer ) CLASS vfFileRead
hb_vfSeek( ::pHandle, nPointer, FS_BEGINNING )
::nError := FError()
RETURN ::nError == 0
*******************************************************************
METHOD GetLastLinePointer() CLASS vfFileRead
Local nCurrentPointer := ::GetPointer()
Local nCurrentLine := ::nCurrLine
Local lCurrentEOF := ::lEOF
Local nLastLinePointer
::GoBottom()
nLastLinePointer := ::GetPointer()
::SetPointer ( nCurrentPointer ) //roll back pointer
::nCurrLine := nCurrentLine //roll back ::nCurrLine
::lEOF := lCurrentEOF //roll back ::lEOF
::nError := FError()
RETURN nLastLinePointer
*******************************************************************
METHOD CurrentLine() CLASS vfFileRead
RETURN ::nCurrLine
*******************************************************************
METHOD CountLines( xGauge ) CLASS vfFileRead
Local cLine := ""
Local nCurrentPos := ::GetPointer()
Local nLastPos := hb_vfSeek( ::pHandle, 0, FS_END )
Local nSkips := 0
Local nlDelim := Len ( ::cDelim )
Local nPrevPos := 0
Local nBuffSize := 64 * 1024
Local exCurrentGauge := ::exGauge
Local nPointer := 0
Local nStart, nPos
IF HB_ISEVALITEM( xGauge )
::exGauge := xGauge
ENDIF
::aRecordsMap := {}
::GoTop()
DO WHILE !hb_vfEof( ::pHandle )
nStart := 1
nPointer := ::GetPointer()
cLine := hb_vfReadLen( ::pHandle, nBuffSize )
DO WHILE Right (cLine, 1) $ ::cDelim .AND. !hb_vfEof( ::pHandle ) //Avoid splitting of end-of-line marks in the buffer.
cLine += hb_vfReadLen( ::pHandle, nlDelim )
ENDDO
DO WHILE (nPos := hb_AT( ::cDelim, cLine, nStart )) > 0
nSkips ++
IF nSkips % ::nRecordsMapStep == 0
AAdd( ::aRecordsMap, { ULLShrink( ULL2Bin( nSkips ) ), ULLShrink( ULL2Bin( nPointer + nPrevPos - 1 /* pointer starts from 0 not from 1 like AT( ) */ ) ) } )
IF HB_ISEVALITEM( ::exGauge )
Eval( ::exGauge, nPointer + nPrevPos - 1, nLastPos, nSkips , 'Processing', Self )
ENDIF
ENDIF
nStart := nPos + nlDelim
nPrevPos := nStart
ENDDO
ENDDO
IF .NOT. Right( cLine, nlDelim ) == ::cDelim .AND. nLastPos > 0 //The last line does not end with a delimiter. Make it the last record.
nSkips ++
ENDIF
IF HB_ISEVALITEM( ::exGauge )
Eval( ::exGauge, nPointer + nPrevPos - 1, nLastPos, nSkips , 'Done', Self )
ENDIF
::nLastLine := nSkips
::SetPointer ( nCurrentPos )
::nError := FError()
::exGauge := exCurrentGauge
RETURN nSkips
*******************************************************************
METHOD SeekLine( cSeekString, nFromRecord, lCaseSensitive, xGauge ) CLASS vfFileRead
Local cLine := ""
Local nCurrentPos := ::GetPointer()
Local nLastPos := hb_vfSeek( ::pHandle, 0, FS_END )
Local nSkips := 0
Local nlDelim := Len ( ::cDelim )
Local nPrevPos := 0
Local nBuffSize := 64 * 1024
Local exCurrentGauge := ::exGauge
//Local nPointer := 0
Local nFoundLine := 0
Local nStart, nPos, nPointer
Default cSeekString := "", nFromRecord := 1, lCaseSensitive := .T.
::SetPointer ( nCurrentPos )
IF Empty ( cSeekString ) .OR. cSeekString == ::cDelim
RETURN nFoundLine <> 0
ENDIF
IF !lCaseSensitive
cSeekString := HMG_Upper ( cSeekString )
ENDIF
IF HB_ISEVALITEM( xGauge )
::exGauge := xGauge
ENDIF
::GoTo( nFromRecord )
DO WHILE !hb_vfEof( ::pHandle ) .AND. nFoundLine == 0
nStart := 1
nPointer := ::GetPointer()
cLine := hb_vfReadLen( ::pHandle, nBuffSize )
//Avoid splitting of whole line in the buffer.
nPos := hb_RAt ( ::cDelim, cLine )
IF nPos > 0
cLine := Left ( cLine, nPos + nlDelim - 1)
::SetPointer ( nPointer + Len ( cLine ) )
ENDIF
IF !lCaseSensitive
cLine := HMG_Upper ( cLine )
ENDIF
DO WHILE (nPos := hb_AT( ::cDelim, cLine, nStart )) > 0
IF hb_AT( cSeekString, cLine, nStart, nPos ) > 0
nFoundLine := ::nCurrLine + nSkips
EXIT
ENDIF
IF (::nCurrLine + nSkips) % ::nRecordsMapStep == 0
IF hb_AScan( ::aRecordsMap, { |x| Bin2ULL ( ULLUnShrink ( x [ 1 ] ) ) == (::nCurrLine + nSkips) } ) == 0
AAdd( ::aRecordsMap, { ULLShrink( ULL2Bin( (::nCurrLine + nSkips) ) ), ULLShrink( ULL2Bin( nPointer + nPrevPos - 1 /* pointer starts from 0 not from 1 like AT( ) */ ) ) } )
ENDIF
IF HB_ISEVALITEM( ::exGauge )
Eval( ::exGauge, nPointer + nPrevPos - 1, nLastPos, ::nCurrLine + nSkips , 'Processing', Self )
ENDIF
ENDIF
nStart := nPos + nlDelim
nPrevPos := nStart
nSkips ++
ENDDO
ENDDO
IF HB_ISEVALITEM( ::exGauge )
Eval( ::exGauge, nPointer + nPrevPos - 1, nLastPos, ::nCurrLine + nSkips , 'Done', Self )
ENDIF
::SetPointer ( nCurrentPos )
IF nFoundLine <> 0
::GoTo ( nFoundLine )
ENDIF
::nError := FError()
::exGauge := exCurrentGauge
cLine := ""
RETURN nFoundLine <> 0
*******************************************************************
METHOD GetRecordsMap() CLASS vfFileRead
RETURN ::aRecordsMap
*******************************************************************
METHOD PutRecordsMap( aMap ) CLASS vfFileRead
IF aMap == NIL .OR. !hb_isArray( aMap )
RETURN Self
ENDIF
::aRecordsMap := aMap
RETURN Self
*******************************************************************
Function ULLShrink( cBin )
RETURN REMRIGHT( cBin, CHR(0) )
*******************************************************************
Function ULLUnShrink( cBin )
RETURN PADRIGHT( cBin, 8, CHR(0) )
*******************************************************************
Function DoNothing
Local Nothing_s
Nothing_s := "Nothing"
Return NIL
Function GoToRecord( oFile, RecordNumber_n )
Local Record_n := 0
Record_n := oFile:GoTo( RecordNumber_n )
Return Record_n
//Support Unsigned Long Long
#pragma BEGINDUMP
#include "hbapi.h"
#include "hbapiitm.h"
#include <mgdefs.h>
#include <psapi.h>
// SetParent (hWndChild, hWndNewParent)
HB_FUNC ( SETPARENT )
{
HWND hWndChild = (HWND) HB_PARNL (1);
HWND hWndNewParent = (HWND) HB_PARNL (2);
hb_retnl ((LONG_PTR) SetParent (hWndChild, hWndNewParent) );
}
HB_FUNC( BIN2ULL )
{
PHB_ITEM pItem = hb_param( 1, HB_IT_STRING );
HB_U64 uiResult = 0;
if( pItem )
{
HB_SIZE nLen = hb_itemGetCLen( pItem );
if( nLen )
{
const char * pszString = hb_itemGetCPtr( pItem );
uiResult = HB_GET_LE_UINT64( pszString );
}
}
hb_retnint( uiResult );
}
HB_FUNC( ULL2BIN )
{
char szResult[ 8 ];
HB_U64 uiValue = ( HB_U64 ) hb_parnint( 1 );
HB_PUT_LE_UINT64( szResult, uiValue );
hb_retclen( szResult, 8 );
}
#pragma ENDDUMP
************************ THE END *********************