Link to home
Start Free TrialLog in
Avatar of DanDaemon
DanDaemonFlag for Ukraine

asked on

Need make Seek function for text file...

Hello guys,

I have a problem with Seek with Textfile files...

I have text XML file:
<?xml version="1.0" encoding= "UTF-8"?>'
<history version="1.3">
</history>

I need to add node before </history> but using standard I/O functions... Then I need to assign file handle to this file, open it for read/write, seek to end of file. But I have </history> there.... I need to write string like <item>...</item> before tag </history>...

Is it possible to do without using XML parser?

Thanks!
Avatar of DanDaemon
DanDaemon
Flag of Ukraine image

ASKER

I need use standard I/O functions, e.g. command append becuase my XML file can be more than 2GB!
2 GB ? Maybe you better use DataBase insted of XML ?
I can't use database. I need write XML file.
Avatar of TheRealLoki
I just wrote this for you, let me know if it does what you want
can't "insert" the data, but can make a 2nd file from teh first (then replace original)
has to go through the whole 2gig i'm afraid, but i can't think of a faster way
Easy enough to add a progress bar to it
procedure TForm1.Button1Click(Sender: TObject);
begin
  InsertXMLTagHeader('file.xml', 'history', 'item', #13#10'<note>this was changed to include item tag</note>'#13#10, 1024, 1024);
end;
 
procedure TForm1.InsertXMLTagHeader(Filename, InsertAroundThisTag, TagName, ExtraDataInsideTag: string; ExpectToFindOpenTag, ExpectToFindEndTag: longint);
const
  MaxBufferLen = 1; //6384;
var
  FSin, FSOut: TFileStream;
  b: byte;
  s: string;
  opentagtype1, opentagtype2, closetag: string;
  success: boolean;
  foundstartat, foundendat, cnt: longint;
  buffer: array[0..MaxBufferLen-1] of byte;
  amounttoread: longint;
begin
  opentagtype1 := '<' + uppercase(InsertAroundThisTag) + '>';
  opentagtype2 := '<' + uppercase(InsertAroundThisTag) + ' ';
  closetag := '</' + uppercase(InsertAroundThisTag) + '>';
  success := false;
  s := '';
  cnt := 0;
  foundstartat := -1;
  foundendat := -1;
  FSin := TFileStream.Create(filename, fmOpenRead OR fmShareDenyNone);
  FSOut := TFileStream.Create(filename + '.tmp', fmCreate OR fmShareExclusive);
  try
    repeat
      inc(cnt);
      FSin.Read(b, sizeof(b));
      if (foundstartat > -1) then
      begin // we already have a partial match, keep going
        s := s + chr(b);
        if ( (uppercase(s) = opentagtype1) or (uppercase(s) = opentagtype2) ) then
        begin
          success := true; // we have found the entire tag start
        end
        else if ( (pos(uppercase(s), opentagtype1) = 1) or (pos(uppercase(s), opentagtype1) = 1) ) then
        begin
// we have found another character in the sequence, keep going
        end
        else
        begin // broken the partial match, write what we have so far, and start again
          foundstartat := -1; //
          FSOut.Write(s[1], length(s));
          s := '';
        end;
      end
      else if (uppercase(chr(b)) = opentagtype1[1]) then
      begin // found the start of a match, save it and keep looking
        s := chr(b);
        foundstartat := FSIn.Position-1;
      end
      else
        FSOut.Write(b, sizeof(b));
    until (success or (cnt > ExpectToFindOpenTag));
    if success then
    begin // now find the end
      success := false;
      cnt := 0;
      repeat
        inc(cnt);
        FSIn.Seek(0-cnt, soFromEnd);
        FSin.Read(b, sizeof(b));
        if (foundendat > -1) then
        begin // we already have a partial match, keep going
          s := chr(b) + s;
          if (uppercase(s) = closetag) then
          begin
            success := true; // we have found the entire tag end
          end
          else if (pos(uppercase(s), closetag) <> 0) then
          begin
  // we have found another character in the sequence, keep going
          end
          else
          begin // broken the partial match, start again
            foundendat := -1; //
            s := '';
          end;
        end
        else if (uppercase(chr(b)) = closetag[length(closetag)]) then
        begin // found the start (end actually) of a match, save it and keep looking
          s := chr(b);
          foundendat := FSIn.Position-1;
        end
      until (success or (cnt > ExpectToFindEndTag));
      if success then
      begin // now insert our new data, then read until the end tag, insert our end tag then the rest of the file
        s := '<' + TagName + '>' + ExtraDataInsideTag;
        FSOut.Write(S[1], length(S));
        FSIn.Seek(foundstartat, soFromBeginning);
        repeat
          amounttoread := MaxBufferLen;
          if ( (FSIn.Position + MaxBufferLen) > foundendat) then
            amounttoread := foundendat - FSIn.Position;
          FSIn.Read(buffer, amounttoread);
          FSOut.Write(buffer, amounttoread);
        until FSIn.Position >= foundendat;
        s := '</' + TagName + '>';
        FSOut.Write(S[1], length(S));
        repeat
          amounttoread := MaxBufferLen;
          if ( (FSIn.Position + MaxBufferLen + 1) >= FSIn.Size) then
            amounttoread := FSIn.Size - FSIn.Position - 1;
          FSIn.Read(buffer, amounttoread);
          FSOut.Write(buffer, amounttoread);
        until (FSIn.Position + 1) = FSIn.Size;
 
 
      end;
    end
    else
      Raise Exception.Create(Format('did not find "%s" within %d bytes', [InsertAroundThisTag, ExpectToFindOpenTag]));
  finally
    FSin.Free;
    FSOut.Free;
  end;
  if FileExists(filename + '.tmp') then
  begin
//    RenameFile(filename, filename + '.old');
//    RenameFile(filename + '.tmp', filename);
//    DeleteFile(filename + '.old');
  end
end;

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Geert G
Geert G
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks! This is what I need!