Hurtig streng manipulation til XML parser
Jeg sidder og roder med en OpenSource XML-parser til Delphi. Sourcen til denne kan hentes på http://www.delphihome.com/xml/ - men den er ikke programmeret helt færdigt - den kan gemme XML-filer, men ikke læse dem ind igen.Projektet hedder XMLWorks, og hvad jeg så har lavet, er en lille udvidelse, så den kan læse sine egne filer ind igen. Her kommer så performance ind i billedet - for en XML-fil med 2000 poster (ikke så utænkeligt endda...) tager forholdsvist lang tid at parse. Jeg har lavet 2 implementationer, en baseret på strings og en baseret på PChars - og på min temmelig store maskine med rigeligt ram tager det ca. 16 sekunder at parse med PChar-løsningen og ca. 11 sekunder med string løsningen.
Nu har jeg jo altid troet at PChar var mere effektiv end Delphi's indbyggede strenge.. Det er da i hvert fald hvad jeg har hørt. Men jeg kan også godt se, efter jeg har optimeret så meget som jeg fåmår, at der er lidt uhensigtsmæssigheder i min XML implementering.
Nå, tid til spørgsmålet: Nedenfor har jeg pastet mine to implementeringer - hent XMLWorks og sæt dem ind i sourcen dertil (jeg kan evt. sende dig et eksempelprojekt). Hjælp mig så med at få presset maximal performance ud af det forhåndenværende :)
På forhånd tak :)
{$IFDEF str}
procedure TXMLCollection.ParseXML(const Value: string);
//String implementeringen
// Previous: TXMLCollectionItem(Add).ParseXML(PChar(Value));
var
XMLList : string;
StartIndex,
StopIndex : integer;
SearchStrStart,
SearchStrStop : string;
begin
SearchStrStart := '<' + DTDName + '>';
StartIndex := pos(SearchStrStart, value);
SearchStrStop := '</' + DTDName + '>';
StopIndex := pos(SearchStrStop, value);
XMLList := copy(value, StartIndex, StopIndex - StartIndex +
length(SearchStrStop));
SearchStrStart := '<' + TXMLCollectionItemClass(ItemClass).GetElementName +
'>';
SearchStrStop := '</' + TXMLCollectionItemClass(ItemClass).GetElementName +
'>';
while pos(SearchStrStart, XMLList) > 0 do
begin
StartIndex := pos(SearchStrStart, XMLList);
StopIndex := pos(SearchStrStop, XMLList);
TXMLCollectionItem(Add).ParseXML(PChar(copy(XMLList, StartIndex, StopIndex -
StartIndex + length(SearchStrStop))));
XMLList := copy(XMLList, StopIndex + length(SearchStrStop),
length(XMLList));
end;
end;
{$ENDIF}
{$IFNDEF str}
procedure TXMLCollection.ParseXML(const Value: string);
//PChar løsningen:
// Previous: TXMLCollectionItem(Add).ParseXML(PChar(Value));
var
pDTDName : PChar;
pXMLList : PChar;
pSearchStr : PChar;
pCloseElementName,
pElementName : PChar;
CloseElementLength: integer;
Length : cardinal;
begin
pSearchStr := PChar(value);
pDTDName := PChar('<' + DTDName + '>');
pXMLList := strpos(pSearchStr, pDTDName); //find the start index of the list
Length := strpos(pXMLList, PChar('</' + DTDName + '>')) - pXMLList; //find the length of the data-list
StrLCopy(pXMLList, pXMLList, Length + strlen(pDTDName) + 1); //removes everything AFTER the list
if Length > 0 then //data in the list to be processed
begin
pElementName := PChar('<' + TXMLCollectionItemClass(ItemClass).GetElementName
+ '>');
pCloseElementName := PChar('</' +
TXMLCollectionItemClass(ItemClass).GetElementName + '>');
CloseElementLength := strlen(pCloseElementName);
//initialize variable:
pXMLList := strpos(pXMLList, pElementName);
while pXMLList <> nil do
begin
pXMLList := strpos(pXMLList, pElementName); //crop up to the next element
StrLCopy(pSearchStr, pXMLList, strpos(pXMLList, pCloseElementName) -
pXMLList + CloseElementLength); //return in pSearchStr, the current post to be searched
inc(pXMLList, strlen(pSearchStr));
TXMLCollectionItem(Add).ParseXML(pSearchStr);
pXMLList := strpos(pXMLList, pElementName);
end;
end;
end;
{$ENDIF}