我正在寻找一种非常快速的Delphi XML解析器,用于非常简单的数据。
考虑以下类型的数据:
<node>
<datatype1>randomdata</datatype1>
<datatype2>randomdata</datatype2>
<datatype3>randomdata</datatype3>
<datatype4>randomdata</datatype4>
<datatype5>randomdata</datatype5>
<datatype6>randomdata</datatype6>
<datatype7>randomdata</datatype7>
<datatype8>randomdata</datatype8>
<datatype9>randomdata</datatype9>
<datatype10>randomdata</datatype10>
<datatype11>randomdata</datatype11>
<datatype12>randomdata</datatype12>
<datatype13>randomdata</datatype13>
<datatype14>randomdata</datatype14>
<datatype15>randomdata</datatype15>
<datatype16>randomdata</datatype16>
<datatype17>randomdata</datatype17>
<datatype18>randomdata</datatype18>
<datatype19>randomdata</datatype19>
<datatype20>randomdata</datatype20>
</node>
复制10000次(数据类型和数据在实际场景中明显不同)。还要考虑数据包含Unicode。
这将被解析并加载到像
这样的记录数组中Type MyData = record
d1,d2,d3,d4,d5,
d6,d7,d8,d9,d10,
d11,d12,d13,d14,d15,
d16,d17,d18,d19,d20: string;
end;
我为此编写了一个自定义解析器,在我的计算机中占用大约。整个过程115ms,从加载文件到填写10,000条记录。
所以我正在寻找能够更快完成任务的东西。
相关问题:
答案 0 :(得分:9)
首先让我告诉你,你在这里优化错误的东西:除非你是为了娱乐目的而这样做,否则你的做法是错误的。 XML并不是一个困难的格式,但它确实有它的怪癖,它需要它的自由。它是专为外国应用程序之间的数据交换而设计的格式,因此需要强调兼容性,而不是SPEED!当面对略微改变的XML文件时,非标准的超快速解析器有什么用呢?
如果你能找到一个XML解析LIBRARY,它可以保证与那些可以在HALF上解析你的数据的任何东西兼容,你的硬盘可以读取它的速度,然后简单地实现生产者 - 消费者多线程应用程序,其中一个线程不断从磁盘读取数据,而另外两个只是解析。最后,您只能受到硬盘速度的限制,同时保持兼容性。如果您只是在寻找速度,那么您可能会犯错误,跳过XML功能,取决于您正在处理的示例XML文件的某些特性。您的申请可能会因多种原因而中断。
请记住,应用程序最昂贵的周期是维护,而不是生产。如果计算机速度提高50%(在竞争对手中取消优势),那么今天你可能会获得的速度提高50%,而维护困难的速度提高200%则会在一年左右的时间内丢失。此外,没有必要超过这些过程的自然限制,如硬盘的速度。与使用RAM驱动器中的文件进行测试无关 - 当应用程序投入生产时,它将与HDD中的文件一起使用,并且您的应用程序的性能将受到HDD速度的限制。
无论如何,我偶尔喜欢挑战,我真的很喜欢解析器。接下来是一个非常简单的解析器实现,它只查看输入字符串中的每个字符,只复制需要的东西:复制节点的名称以决定下一步做什么并复制适当时节点的“有效负载”,以便将其推入阵列。在我的“谦虚”i7 @ 3.4 Ghz解析通过复制样本数据而构建的字符串10,000次需要63 ms。它显然胜过你的时间,但是一句警告,这段代码很脆弱:依赖有一个XML文件是某种形式。没办法。
program Project28;
{$APPTYPE CONSOLE}
uses SysUtils, DateUtils, Windows;
const SampleData =
'<node>'#13#10+
' <datatype1>randomdata</datatype1>'#13#10+
' <datatype2>randomdata</datatype2>'#13#10+
' <datatype3>randomdata</datatype3>'#13#10+
' <datatype4>randomdata</datatype4>'#13#10+
' <datatype5>randomdata</datatype5>'#13#10+
' <datatype6>randomdata</datatype6>'#13#10+
' <datatype7>randomdata</datatype7>'#13#10+
' <datatype8>randomdata</datatype8>'#13#10+
' <datatype9>randomdata</datatype9>'#13#10+
' <datatype10>randomdata</datatype10>'#13#10+
' <datatype11>randomdata</datatype11>'#13#10+
' <datatype12>randomdata</datatype12>'#13#10+
' <datatype13>randomdata</datatype13>'#13#10+
' <datatype14>randomdata</datatype14>'#13#10+
' <datatype15>randomdata</datatype15>'#13#10+
' <datatype16>randomdata</datatype16>'#13#10+
' <datatype17>randomdata</datatype17>'#13#10+
' <datatype18>randomdata</datatype18>'#13#10+
' <datatype19>randomdata</datatype19>'#13#10+
' <datatype20>randomdata</datatype20>'#13#10+
'</node>'#13#10;
const NodeIterations = 10000;
type
TDummyRecord = record
D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13,
D14, D15, D16, D17, D18, D19, D20: string;
end;
TDummyRecordArray = array[1..NodeIterations] of TDummyRecord;
procedure ParseDummyXMLToRecordArray(const InputText:string; var A: TDummyRecordArray);
var PInputText: PChar;
cPos, TextLen: Integer;
C: Char;
State: Integer;
tag_starts_at: Integer;
last_payload_starts_at: Integer;
FlagEndTag: Boolean;
NodeName, Payload: string;
cNode: Integer;
const st_not_in_node = 1;
st_in_node = 2;
begin
cPos := 1;
TextLen := Length(InputText);
PInputText := @InputText[1];
State := st_not_in_node;
last_payload_starts_at := 1;
cNode := 0;
// This is the lexer/parser loop. It's a finite-state machine with only
// two states: st_not_in_node and st_in_node
while cPos < TextLen do
begin
C := PInputText[cPos-1];
case State of
// What happens when we're NOT currently inside a node?
// Not much. We only jump to st_in_node if we see a "<"
st_not_in_node:
case C of
'<':
begin
// A node starts here. Switch state and set up some simple
// flags.
state := st_in_node;
tag_starts_at := cPos + 1;
FlagEndTag := False;
end;
end;
// What happens while inside a node? Again, not much. We only care about
// the "/" - as it signals an closing tag, and we only care about the
// ">" because that means the end of the ndoe.
st_in_node:
case C of
'/': FlagEndTag := True;
'>':
begin
// This is where the magic haepens. We're in one of possibly two states:
// We're ither seeing the first <name> of a pair, or the second </name>
//
if FlagEndTag then
begin
// This is the closing pair of a tag pair, ie, it's the </NodeName> What we'll do
// depends on what node is closing, so we retreive the NodeName:
NodeName := System.Copy(InputText, tag_starts_at+1, cPos - tag_starts_at-1);
if NodeName <> 'node' then // SAMPLE-DATA-SPECIFIC: I know I don't care about "node" tags.
begin
// SAMPLE-DATA-SPECIFIC: I know there are only two kinds of nodes:
// "node" and "datatypeN". I retreive the PAYLOAD for the node because
// I know it's not "ndoe" and I know I'll need it.
Payload := System.Copy(InputText,last_payload_starts_at, tag_starts_at - last_payload_starts_at -1);
// Make sure we're dealing with a valid node
if (cNode > 0) and (cNode <= High(A)) then
begin
// Based on NodeName, copy the Payload into the appropriate field.
if NodeName = 'datatype1' then A[cNode].D1 := Payload
else if NodeName = 'datatype2' then A[cNode].D2 := Payload
else if NodeName = 'datatype3' then A[cNode].D3 := Payload
else if NodeName = 'datatype4' then A[cNode].D4 := Payload
else if NodeName = 'datatype5' then A[cNode].D5 := Payload
else if NodeName = 'datatype6' then A[cNode].D6 := Payload
else if NodeName = 'datatype7' then A[cNode].D7 := Payload
else if NodeName = 'datatype8' then A[cNode].D8 := Payload
else if NodeName = 'datatype9' then A[cNode].D9 := Payload
else if NodeName = 'datatype10' then A[cNode].D10 := Payload
else if NodeName = 'datatype11' then A[cNode].D11 := Payload
else if NodeName = 'datatype12' then A[cNode].D12 := Payload
else if NodeName = 'datatype13' then A[cNode].D13 := Payload
else if NodeName = 'datatype14' then A[cNode].D14 := Payload
else if NodeName = 'datatype15' then A[cNode].D15 := Payload
else if NodeName = 'datatype16' then A[cNode].D16 := Payload
else if NodeName = 'datatype17' then A[cNode].D17 := Payload
else if NodeName = 'datatype18' then A[cNode].D18 := Payload
else if NodeName = 'datatype19' then A[cNode].D19 := Payload
else if NodeName = 'datatype20' then A[cNode].D20 := Payload
else
raise Exception.Create('Unknown node: ' + NodeName);
end
else
raise Exception.Create('cNode out of bounds.');
end;
// Repeat :-)
state := st_not_in_node;
end
else
begin
// Node start. Retreive node name. I only care about the start of the "NODE" - if I see that
// I'll increment the current node counter so I'll go on filling the next position in the array
// with whatever I need.
NodeName := System.Copy(InputText, tag_starts_at, cPos - tag_starts_at);
last_payload_starts_at := cPos+1;
if NodeName = 'node' then Inc(cNode);
state := st_not_in_node;
end;
end;
end;
end;
Inc(cPos);
end;
end;
var DataString: string;
SB: TStringBuilder;
i: Integer;
DummyArray: TDummyRecordArray;
T1, T2, F: Int64;
begin
try
try
// Prepare the sample string; 10.000 iterations of the sample data.
SB := TStringBuilder.Create;
try
for i:=1 to NodeIterations do
SB.Append(SampleData);
DataString := SB.ToString;
finally SB.Free;
end;
// Invoke the simple parser using the string constant.
QueryPerformanceCounter(T1);
ParseDummyXMLToRecordArray(DataString, DummyArray);
QueryPerformanceCounter(T2);
QueryPerformanceFrequency(F);
WriteLn(((T2-T1) * 1000) div F);
// Test parse validity.
for i:=1 to NodeIterations do
begin
if DummyArray[i].D1 <> 'randomdata' then raise Exception.Create('Bug. D1 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D2 <> 'randomdata' then raise Exception.Create('Bug. D2 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D3 <> 'randomdata' then raise Exception.Create('Bug. D3 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D4 <> 'randomdata' then raise Exception.Create('Bug. D4 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D5 <> 'randomdata' then raise Exception.Create('Bug. D5 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D6 <> 'randomdata' then raise Exception.Create('Bug. D6 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D7 <> 'randomdata' then raise Exception.Create('Bug. D7 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D8 <> 'randomdata' then raise Exception.Create('Bug. D8 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D9 <> 'randomdata' then raise Exception.Create('Bug. D9 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D10 <> 'randomdata' then raise Exception.Create('Bug. D10 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D11 <> 'randomdata' then raise Exception.Create('Bug. D11 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D12 <> 'randomdata' then raise Exception.Create('Bug. D12 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D13 <> 'randomdata' then raise Exception.Create('Bug. D13 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D14 <> 'randomdata' then raise Exception.Create('Bug. D14 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D15 <> 'randomdata' then raise Exception.Create('Bug. D15 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D16 <> 'randomdata' then raise Exception.Create('Bug. D16 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D17 <> 'randomdata' then raise Exception.Create('Bug. D17 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D18 <> 'randomdata' then raise Exception.Create('Bug. D18 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D19 <> 'randomdata' then raise Exception.Create('Bug. D19 doesn''t have the proper value, i=' + IntToStr(i));
if DummyArray[i].D20 <> 'randomdata' then raise Exception.Create('Bug. D20 doesn''t have the proper value, i=' + IntToStr(i));
end;
except on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;
finally
WriteLn('ENTER to Exit');
ReadLn;
end;
end.
答案 1 :(得分:1)
如果您的XML非常简单,并且格式是固定的,并且文件很大,并且您需要非常快速的处理,我建议您自己实现解析,简单的while(i&lt; length(unputStr))do周期。在那里你可以搜索'&lt;'符号,提取节点名称等等。