Solved

Searching through a big file.

Posted on 2008-10-27
36
285 Views
Last Modified: 2012-05-05
Hi

I was wondering what can be the most efficient way to search through the contents of a large binary data file (eg. 10GB) to find a specified string ?
0
Comment
Question by:tadzio_blah
  • 13
  • 7
  • 6
  • +3
36 Comments
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 22819372
any specifics about the binary file ?
0
 

Author Comment

by:tadzio_blah
ID: 22819402
nope... none... some random data - just need to find the specified string in it.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 22819658
start to search from several locations with threads
keep in mind that the io will be the bottleneck
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 22819677
0
 
LVL 5

Expert Comment

by:wd123
ID: 22820431
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22821963
Try the code below:

John

function MemPos(const Filename, TextToFind: string; iFrom: cardinal = 0;

         CaseSensitive: boolean = False): int64;

var

  buffer : string;

  len : integer;

  MS : TMemoryStream;

  charsA, charsB : set of char;

  p, pEnd : PChar;

begin

  result := -1;

  if (TextToFind <> '') and FileExists(Filename) then

   begin

    len := Length(TextToFind);

    SetLength(buffer, len);

    if CaseSensitive then

     begin

      charsA := [TextToFind[1]];

      charsB := [TextToFind[len]];

    end

    else

     begin

      charsA := [ AnsiLowerCase(TextToFind)[1],   AnsiUpperCase(TextToFind)[1] ];

      charsB := [ AnsiLowerCase(TextToFind)[len], AnsiUpperCase(TextToFind)[len]];

    end;

    MS := TMemoryStream.Create;

    try

      MS.LoadFromFile(Filename);

      if (len <= MS.Size) and (iFrom <= (MS.Size - len) + 1) then

       begin

        p := MS.Memory;

        pEnd := p;

        Inc(p, iFrom);

        Inc(pEnd, (MS.Size - len) + 1);

        while (p <= pEnd) and (result < 0)

        do

         begin

          if (p^ in charsA) and ((p +len -1)^ in charsB) then

           begin

            MoveMemory(@buffer[1], p, len);

            if (CaseSensitive and (AnsiCompareStr(TextToFind, buffer) = 0)) or

                 (AnsiCompareText(TextToFind, buffer) = 0) then

                   result := p -MS.Memory;

           end;

          Inc(p);

         end;

       end;

    finally

      MS.Free;

    end;

  end;

end;
 
 

// Usage
 

if MemPos(Path + File.Name, TextToFind.Text, 0 , CaseSense.Checked) > 0 then

 begin

   .... do whatever

 end;

Open in new window

0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22821988
PS..

I have tried this on CD ISO files 650 MB and a few DVD ISOs > 1GB.

For really large files, it will take some time! Not certain how to optimize it further.

John
0
 

Author Comment

by:tadzio_blah
ID: 22822858
Let's say that it can't load more than 100mb of that file at one time (because i don't think that loading). The code you provided only works when file size < system memory size.
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22822906
Did you try it?

Windows will cache memory to the hard disk. Your virtual memory settings will control the amount.

John
0
 

Author Comment

by:tadzio_blah
ID: 22823005
yes, i've tried it, that's way i wrote the previous comment. i'm getting an out of memory exception.
Snap1.jpg
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22823090
Hmmm... your PC should have cached it in virtual memory... I think. Check your settings there and increase as necessary.

And, sorry I didn't get out of your comment that you tried it as by writing, "Let's say that it can't load more than 100mb...' sounds hypothetical. The misleading part was "let's say".

I do have a lot of RAM on my development PCs so that could very well be a problem for you as it is an in memory function.

Sorry this did not work out for you. I tried but everyone has different needs and machines!

John
0
 

Author Comment

by:tadzio_blah
ID: 22823231
I have 4GB of mem on my dev laptop.
Did you trie to load a file >6GB with that function ?
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22823240
Well, the way I see it is that your going to have read the file in blocks. At the 10gb size using a memory stream that size is not a good idea. Your going to have to read the files in blocks(16-128Kb)'s at a time and search through each of those blocks.

I'll give it a whirl but the catch here is that the data might be split between two blocks.
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22823243
I do have a question, is the data your searching for normal strings? I.E. 'This is a string'#0?
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 22823245
i won't load more than 2 gig or maybe 4 gig (windows boundary)

you need to use a TFileStream and start reading from a certain point

i'll try an example
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22823255
Nope... my largest was a DVD ISO image. 1.2 gig + or -.

John
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 22823262
oops, looks there is more than 1 expert with that idea
0
 

Author Comment

by:tadzio_blah
ID: 22823274
and my vmem size is to min 6gb and max 20gb... so that shouldn't be a problem
btw i'm using D2007 - maybe that's the problem (maybe they've chenged something in the memstream that it has to fit the available physical mem).
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:tadzio_blah
ID: 22823302
i'm searching for a standard delphi string in those files
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22823663
This is my go, probably have some errors as I didn't get to test it.
function StrChr(pStr: PChar; ch: char; Length: DWORD): PChar;

var

  EndPtr : Pointer;

begin

  EndPtr := pStr + Length;

  Result := nil;

  While (Addr(pStr) <> EndPtr) Do

    begin

    If (pStr^ = ch) Then

      begin

      Result := pStr;

      Exit;

    end;

    Inc(pStr);

  end;

end;
 

function GetTextOffset(const FileName: PChar; TextToFind: PChar; TextLength: DWORD; BlockSize: DWORD = $4000): Int64;

var

  FileHandle : DWORD;

  Buffer : PChar;

  Data : PChar;

  BytesRead : DWORD;

  BytesToRead : DWORD;

  CharStart,

  CharEnd : Char;

  DoLoop : Bool;

  Offset : Int64;

  Original : Pointer;

  DoInit : Boolean;

begin

  //Initialize result. -1 = String not found

  Result := -1;

  //Open the file read only

  FileHandle := CreateFile(FileName,GENERIC_READ,0,nil,OPEN_EXISTING,0,0);

  //If we could not open the file exit

  If FileHandle = INVALID_HANDLE_VALUE Then Exit;

  Try

    //Allocate memory the size specified

    Buffer := AllocMem(BlockSize);

    //We couldn't get the memory, exit

    If @Buffer = nil Then Exit;

    Try

      //Mark the original buffer address

      Original := @Buffer;

      //Initialize the variable that will exit our loop

      DoLoop := True;

      //Initialize the DoInit variable to false, we'll use this after

      //  the first loop

      DoInit := False;

      //Save this for later

      BytesToRead := BlockSize;

      Repeat

        //initialize the constants back to normal if we didn't find a partial

        If DoInit Then

          begin

          Buffer := Original;

          BytesToRead := BlockSize;

        end;

        //zero the memory of the buffer every reiteration

        ZeroMemory(Buffer,BytesToRead);

        //read the data from the file

        ReadFile(FileHandle,Buffer,BytesToRead,BytesRead,nil);

        //if we didn't read enough we hit the end of the file

        //  so we are done with the loop

        If BytesRead < BytesToRead Then DoLoop := False;

        //We refilled the buffer, let's set it back

        If Not(DoInit) Then

          begin

          Buffer := Original;

        end;

        //increment our current position(easier than calling SetFilePointer)

        Inc(Offset,BytesRead);

        //Search the buffer for the first char in TextToFind

        Buffer := StrChr(Buffer,TextToFind^,BytesRead);

        //the first char isn't there, so restart the loop

        If @Buffer = nil Then Continue;

        //we hit the end of the buffer, not enough room for the string anyway

        If (Buffer + TextLength) >= Original Then

          begin

          //Put what we found, which could be a partial of the TextToFind

          //  in the beginning of the buffer

          Move(Buffer,Original,BlockSize - (Original - Buffer));

          Buffer := Original;

          Inc(Buffer,(BlockSize - (Original - Buffer)));

          Dec(BytesToRead,BlockSize - (Original - Buffer));

          DoInit := False;

          Continue;

        end;

        //Check to see if the last char and string length is matching

        If Buffer[TextLength - 1] = TextToFind[TextLength - 1] Then

          begin

          //Match the actual string

          If StrComp(Buffer,TextToFind) = 0 Then

            begin

            //It matches. Bring the offset back to where Buffer is

            Result := Offset - (Buffer - Original);

            //Exit, we found what we're looking for

            Exit;

          end;

        end;

        DoInit := True;

      Until (Not(DoLoop));

    Finally

      //Deallocate the memory, we're done with it

      FreeMem(Buffer,BlockSize);

    end;

  Finally

    //Close the handle to the file so windows will like us

    CloseHandle(FileHandle);

  end;

end;

Open in new window

0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 300 total points
ID: 22823775

Definitely a case where you can't attempt to load the whole thing in memory (not even using mem mapped files; you could only map portions). The  issue is the memory address space; which is 2GB for 32 bit processes. 3GB if process is set with LARGE ADDRESS AWARE. This means you have to chunck the file in order to do the searching. There are a few optimizations that can be done, such as opening the file for sequential scan, etc. But the biggest bottleneck will be the disk IO. Also, keep in mind that when working with blocks / chuncks, you have to account for the possibility (albeit low) of the search string falling on the "crack"; meaning it falls between two processed blocks. To account for this, you need to use a sliding window where you always slide the last [Search Length - 1] bytes from the end of the current block to the head of the next block to process.

Regards,
Russell

Example provided below:

// Array of Int64 values indicating where the match was found
type
  TFoundMatch       =  Array of Int64;

// Buffer read size: keep in multiples of 1 MB
const
  BLOCK_SIZE        =  1048576;

function FileFindString(FileName: String; Search: String; IgnoreCase: Boolean; out Found: TFoundMatch): Boolean;
var  lpShiftTable:  Array [#0..#255] of LongWord;
     lpCompTable:   Array [#0..#255] of LongWord;
     hOpen:         THandle;
     dwIndex:       LongWord;
     dwRead:        LongWord;
     dwBlock:       LongWord;
     dwSearch:      LongWord;
     dwHold:        Int64;
     lpBuffer:      PChar;
     bMatch:        Boolean;
     cChar:         Char;
begin

  // Clear outbound parameter
  SetLength(Found, 0);

  // Resource protection
  try
     // Get search string length
     dwSearch:=Length(Search);
     // Search string must be specified
     if (dwSearch > 0) then
     begin
        // Attempt to open the file with sequential scan flag
        hOpen:=CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
        // Check open results
        if (hOpen <> INVALID_HANDLE_VALUE) then
        begin
           // Resource protection
           try
              // Need to work with tail char
              cChar:=Search[dwSearch];
              // Initialize comparison table
              FillChar(lpCompTable, SizeOf(lpCompTable), 255);
              // Set last char
              lpCompTable[cChar]:=0;
              // Handle case insensitive matching
              if IgnoreCase then
              begin
                 case cChar of
                    'A'..'Z' :  lpCompTable[Char(Ord(cChar) + 32)]:=0;
                    'a'..'z' :  lpCompTable[UpCase(cChar)]:=0;
                 end;
              end;
              // Initialize the boyer-moore shift table
              for cChar:=#0 to #255 do lpShiftTable[cChar]:=dwSearch;
              // Update shift table
              for dwIndex:=1 to Pred(dwSearch) do
              begin
                 // Set shift for char
                 lpShiftTable[Search[dwIndex]]:=dwSearch - dwIndex;
                 // Set shift for other case of char
                 if IgnoreCase then
                 begin
                    case Search[dwIndex] of
                       'A'..'Z' :  lpShiftTable[Char(Byte(Search[dwIndex]) + 32)]:=dwSearch - dwIndex;
                       'a'..'z' :  lpShiftTable[Char(Byte(Search[dwIndex]) - 32)]:=dwSearch - dwIndex;
                    end;
                 end;
              end;
              // Allocate memory for file chunk and sliding window
              lpBuffer:=AllocMem(BLOCK_SIZE + Succ(dwSearch));
              // Resource protection
              try
                 // Decrement the search length
                 Dec(dwSearch);
                 // Seed the first block
                 if ReadFile(hOpen, lpBuffer^, BLOCK_SIZE, dwRead, nil) then
                 begin
                    // Set hold marker
                    dwHold:=0;
                    // While data to process
                    while (dwRead > 0) do
                    begin
                       // Set block chunck index
                       dwBlock:=dwSearch;
                       // Process chunk (must be as large as search string)
                       while (dwBlock < dwRead) do
                       begin
                          // Compare last byte of search string with text pointer
                          dwIndex:=lpCompTable[lpBuffer[dwBlock]];
                          // Check for match
                          if (dwIndex = 0) then
                          begin
                             // String compare
                             if IgnoreCase then
                                // Case insensitive (last byte already matches)
                                bMatch:=(StrLIComp(Pointer(Search), @lpBuffer[dwBlock - dwSearch], dwSearch) = 0)
                             else
                                // Case sensitive (last byte already matches)
                                bMatch:=(StrLComp(Pointer(Search), @lpBuffer[dwBlock - dwSearch], dwSearch) = 0);
                             // Check match
                             if bMatch then
                             begin
                                // Get current size
                                dwIndex:=Length(Found);
                                // Allocate space on array
                                SetLength(Found, Succ(dwIndex));
                                // Set entry
                                Found[dwIndex]:=dwHold + (dwBlock - dwSearch);
                                // Update block position by full length of search string
                                Inc(dwBlock, Succ(dwSearch));
                                // Continue
                                Continue;
                             end;
                          end;
                          // Update block processing index by shift index
                          Inc(dwBlock, lpShiftTable[lpBuffer[dwBlock]]);
                       end;
                       // If the last block read < block size, then no data left to process
                       if (dwRead < BLOCK_SIZE) then
                          // Done processing
                          break
                       else
                       begin
                          // Set hold marker
                          Inc(dwHold, dwRead - dwSearch);
                          // Need to account for sliding window (slide bytes from end of buffer to head)
                          Move(lpBuffer[dwRead - dwSearch], lpBuffer[0], dwSearch);
                          // Read next block (break on failure)
                          if not(ReadFile(hOpen, lpBuffer[dwSearch], BLOCK_SIZE, dwRead, nil)) then break;
                          // Update read count by the sliding window size
                          Inc(dwRead, dwSearch);
                       end;
                    end;
                 end;
              finally
                 // Free buffer
                 FreeMem(lpBuffer);
              end;
           finally
              // Close the file handle
              CloseHandle(hOpen);
           end;
        end;
     end;
  finally
     // Was the search string found?
     result:=(Length(Found) > 0);
  end;

end;


--- example usage ---
// If string was found, open the file, seek to each result and verify the data at the location
var  Found:         TFoundMatch;
     liPos:         LARGE_INTEGER;
     dwIndex:       Integer;
     hOpen:         THandle;
     dwRead:        DWORD;
     lpBuffer:      Array [0..7] of Char;
begin

  if FileFindString('C:\Backup\VSNet\2.0 SDK\setup.exe', 'install', True, Found) then
  begin
     hOpen:=CreateFile('C:\Backup\VSNet\2.0 SDK\setup.exe', GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
     if (hOpen <> INVALID_HANDLE_VALUE) then
     begin
        try
           for dwIndex:=0 to Pred(Length(Found)) do
           begin
              lpBuffer[7]:=#0;
              Int64(liPos):=Found[dwIndex];
              SetFilePointer(hOpen, liPos.LowPart, @liPos.HighPart, FILE_BEGIN);
              ReadFile(hOpen, lpBuffer[0], 7, dwRead, nil);
              MessageBox(0, PChar(@lpBuffer), nil, MB_OK);
           end;
        finally
           CloseHandle(hOpen);
        end;
     end;
  end;

end;




0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22826671
Russell,

Nice function! Works well. I am anxious to do some speed tests on  < 1 gig files comparing the two functions provided.

One question.....

// Buffer read size: keep in multiples of 1 MB
const
  BLOCK_SIZE        =  1048576;

Shouldn't block_size = 1024000?

That is actually 1 meg...

Just wondering.

John
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22826731
Russell,

Never mind the stupid brain dead question.  duh.... 2 - 20 power = 1,048,576.

Sorry.

You got my points!

John
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22826878
John, no problem ;-)

Hopefully you should find the two pretty close (performance wise) on smaller files. Let me know how it goes.

Russell
0
 

Author Comment

by:tadzio_blah
ID: 22827490
ThievingSix:
your code doesn't work. i'm getting access violation on the line:

        If (Buffer + TextLength) >= Original Then
          begin
          //Put what we found, which could be a partial of the TextToFind
          //  in the beginning of the buffer
>>>          Move(Buffer,Original,BlockSize - (Original - Buffer));
          Buffer := Original;
          Inc(Buffer,(BlockSize - (Original - Buffer)));
          Dec(BytesToRead,BlockSize - (Original - Buffer));
          DoInit := False;
          Continue;
        end;

can you fix that please ? i'd like to do a timetest on both provided functions.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22827675

Btw, if you plan on timing the two functions (which is a good idea), then you need to modify my routine to "even" things out, as I return ALL matches, not just the first one.


                            // Check match
                             if bMatch then
                             begin
                                // Get current size
                                dwIndex:=Length(Found);
                                // Allocate space on array
                                SetLength(Found, Succ(dwIndex));
                                // Set entry
                                Found[dwIndex]:=dwHold + (dwBlock - dwSearch);
                                //  Done
                                exit;
                             end;
----
Russell
0
 

Author Comment

by:tadzio_blah
ID: 22828084
Don't worry ;) I've done it allready.
Just need the working version of ThievingSix's function.
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22828370
Let's see. Add a variable I, type Integer. Then replace the Move with the following:
For I := 0 To (BlockSize - (Original - Buffer)) Do

  begin

  PByteArray(Original)[I] := Buffer[I];

end;

Open in new window

0
 

Author Comment

by:tadzio_blah
ID: 22829521
error in the code. should be:

For i := 0 To (BlockSize - (Original - Buffer)) Do
  begin
  PByteArray(Original)[i] := byte(Buffer[i]);
end;

, but even now i'm getting access violation on: PByteArray(Original)[i] := byte(Buffer[i]); line.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 22829646
off topic:
>>Russell
nice to have your site back
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22831654
Geert,
Thanks, didn't know anyone still used my site.

ThievingSix,
You might want to take another pass through your code. There are a number of pointer errors (still can't get it to run my system). Eg:

//Mark the original buffer address
Original := @Buffer; // should be Original := Buffer;

Also, the ReadFile is failing to read anything due to the missing ^ . Change to:
ReadFile(FileHandle, Buffer^, BytesToRead, BytesRead, nil);

----

Regards,
Russell
0
 

Author Comment

by:tadzio_blah
ID: 22833384
0
 

Author Comment

by:tadzio_blah
ID: 22836340
I've got tired of waiting, and used Russell's code in my app. Thanks.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22836794
I had no problem with you waiting, regardless if you already started using my routine.

I will share this final note though...

In timing tests where I placed the match string at the end of a 1 GB file, 13500 ms of the routine was spent loading the file (avg speed of 60~70 MB / sec, which was consistent with the avg throughput of my SATA 300 drives), and 500ms was spent performing the actual search though the 1 GB of data, for a combined total of 14 seconds. In the end you would find that most of the search routines are fairly comparable; (like I said earlier) the disk IO is the biggest bottleneck.

Regards,
Russell
0
 

Author Comment

by:tadzio_blah
ID: 22847577
your function works fine. i've made some changes to it to better fit my needs.
it returns only one position, it always searches case insensitive, and you can give a starting position of the search (for use with incremental searching).

function szukaj(FileName, Search: string; StartPos : int64) : int64;

const

  BLOCK_SIZE = 10485760;
 

var

  lpShiftTable:  Array [#0..#255] of LongWord;

  lpCompTable:   Array [#0..#255] of LongWord;

  hOpen:         THandle;

  dwIndex:       LongWord;

  dwRead:        LongWord;

  dwBlock:       LongWord;

  dwSearch:      LongWord;

  dwHold:        Int64;

  lpBuffer:      PChar;

  bMatch:        Boolean;

  cChar:         Char;

  SP:            LARGE_INTEGER;
 

begin

  Result := -1;

  dwSearch:=Length(Search);

  if (dwSearch > 0) then

    begin

      hOpen:=CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);

      if (hOpen <> INVALID_HANDLE_VALUE) then

        begin

          try

            if (StartPos > 0) then

              begin

                SP := LARGE_INTEGER(StartPos);

                SetFilePointer(hOpen, Longint(SP.LowPart), @SP.HighPart, FILE_BEGIN);

                pBar.Progress := StartPos;

                Application.ProcessMessages;

              end;

            cChar:=Search[dwSearch];

            FillChar(lpCompTable, SizeOf(lpCompTable), 255);

            lpCompTable[cChar]:=0;

            case cChar of

              'A'..'Z' :  lpCompTable[Char(Ord(cChar) + 32)]:=0;

              'a'..'z' :  lpCompTable[UpCase(cChar)]:=0;

            end;

            for cChar:=#0 to #255 do

              lpShiftTable[cChar]:=dwSearch;

            for dwIndex:=1 to Pred(dwSearch) do

              begin

                lpShiftTable[Search[dwIndex]]:=dwSearch - dwIndex;

                case Search[dwIndex] of

                  'A'..'Z' :  lpShiftTable[Char(Byte(Search[dwIndex]) + 32)]:=dwSearch - dwIndex;

                  'a'..'z' :  lpShiftTable[Char(Byte(Search[dwIndex]) - 32)]:=dwSearch - dwIndex;

                end;

              end;

            lpBuffer:=AllocMem(BLOCK_SIZE + Succ(dwSearch));

            try

              dwSearch := dwSearch - 1;

              if ReadFile(hOpen, lpBuffer^, BLOCK_SIZE, dwRead, nil) then

                begin

                  dwHold:=StartPos;

                  pBar.Progress := pBar.Progress + dwRead;

                  Application.ProcessMessages;

                  while (dwRead > 0) do

                    begin

                      dwBlock:=dwSearch;

                      while (dwBlock < dwRead) do

                        begin

                          dwIndex:=lpCompTable[lpBuffer[dwBlock]];

                          if (dwIndex = 0) then

                            begin

                              bMatch:=(StrLIComp(Pointer(Search), @lpBuffer[dwBlock - dwSearch], dwSearch) = 0);

                              if bMatch then

                                begin

                                  Result:=dwHold + dwBlock;

                                  Exit;

                                end;

                            end;

                          dwBlock := dwBlock + lpShiftTable[lpBuffer[dwBlock]];

                        end;

                        if (dwRead < BLOCK_SIZE) then

                          Break

                        else

                          begin

                            dwHold := dwHold + (dwRead - dwSearch);

                            Move(lpBuffer[dwRead - dwSearch], lpBuffer[0], dwSearch);

                            if not(ReadFile(hOpen, lpBuffer[dwSearch], BLOCK_SIZE, dwRead, nil)) then

                              Break

                            else

                              pBar.Progress := pBar.Progress + dwRead;

                            Application.ProcessMessages;

                            dwRead := dwRead + dwSearch;

                          end;

                    end;

                end;

            finally

              FreeMem(lpBuffer);

            end;

          finally

            CloseHandle(hOpen);

          end;

        end;

    end;

end;

Open in new window

0
 

Author Comment

by:tadzio_blah
ID: 22847584
oh... and the position returned is just behind the search string.
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now