Delphi:Indy TIdTCPClient 读取 XML
Delphi: Indy TIdTCPClient Reading XML
我正在使用 TidTCPClient 打开到服务器的套接字连接。
服务器开始向我发送不同 XML 数据的音调。
但是 ReadLn 对我没有帮助,因为我不知道 xml 在哪里开始和停止。所以我决定使用 Capture。这工作正常。但正如我从文档中读到的那样,捕获也调用了 ReadLn,这意味着它逐行读取。 30~40 行 XML 有时需要 1~2 秒才能阅读。所以我需要更快地阅读它。我可以使用 ReadStream 或任何其他有区别的东西吗?
我现在使用的代码:
procedure TfrmMain.ThreadRun(Sender: TIdThreadComponent);
var
FData: TStringList;
FMemory: TStringStream;
begin
if Client.Connected = False then Exit;
FData := TStringList.Create;
Client.IOHandler.Capture(FData, '', False);
FMemory := TStringStream.Create('',TEncoding.UTF8);
FMemory.WriteString(FData.Text);
CoInitialize(Nil);
QInsert.ParamByName('XMLData').LoadFromStream(FMemory,ftBlob);
QInsert.Execute;
CoUninitialize;
FMemory.Free;
FData.Free;
end;
如您所见,我正在将 XML 数据插入本地 table,这完成得非常快,15ms ~ 60ms,具体取决于 XML 的大小,但是越来越数据真的很蛋疼
Capture()
在循环中调用 ReadLn()
直到读取指定的终止符行(在您的示例中为空行)。
您需要知道每个 XML 文档的结束位置和下一个文档的开始位置。如果每个文档之间没有明显的标记(一个空行就可以,只要每个 XML 文档在空行之前以换行符结尾,并且 [=36 内部不包含任何空行=]), 那么你唯一剩下的选择就是阅读第一个文档的开始标签,继续阅读直到找到相应的结束标签,然后阅读下一个文档的开始标签并等待它的结束标签,依此类推。
在这种情况下,您最好使用支持推送模型的 XML 解析器来处理该问题。您只需从套接字中读取原始字节并将它们按原样推送到解析器中,让它在遇到每个顶级文档元素的开始和结束时告诉您。您可以缓冲解析器在这两个事件之间为您提供的任何数据,使用开始事件 clear/prepare 缓冲区和结束事件 finalize/flush 数据库缓冲区。
如果您不 willing/able 使用预先存在的 XML 库,则必须编写自己的解析器。
更新: 您展示的示例 Capture()
的 ADelim
参数设置为 ''
,只有 XML 文档由 LF LF
或 CR LF CR LF
序列分隔。在这种情况下,您可以将该定界符用作 ReadLn()
的 ATerminator
参数并让它一次读取 1 个整个文档,而不是完全使用 Capture()
:
procedure TfrmMain.ThreadBeforeRun(Sender: TIdThreadComponent);
begin
CoInitialize(Nil);
end;
procedure TfrmMain.ThreadAfterRun(Sender: TIdThreadComponent);
begin
CoUninitialize;
end;
procedure TfrmMain.ThreadRun(Sender: TIdThreadComponent);
var
Xml: String;
FMemory: TStringStream;
begin
// read until a 'CR LF CR LF' sequence is reached...
Xml := TrimRight(Client.IOHandler.ReadLn(CR+LF+CR+LF, IndyTextEncoding_UTF8));
// alternatively:
// Xml := Client.IOHandler.WaitFor(CR+LF+CR+LF, True, False, IndyTextEncoding_UTF8);
FMemory := TStringStream.Create(Xml, TEncoding.UTF8);
try
QInsert.ParamByName('XMLData').LoadFromStream(FMemory, ftBlob);
finally
FMemory.Free;
end;
QInsert.Execute;
end;
我正在使用 TidTCPClient 打开到服务器的套接字连接。 服务器开始向我发送不同 XML 数据的音调。 但是 ReadLn 对我没有帮助,因为我不知道 xml 在哪里开始和停止。所以我决定使用 Capture。这工作正常。但正如我从文档中读到的那样,捕获也调用了 ReadLn,这意味着它逐行读取。 30~40 行 XML 有时需要 1~2 秒才能阅读。所以我需要更快地阅读它。我可以使用 ReadStream 或任何其他有区别的东西吗?
我现在使用的代码:
procedure TfrmMain.ThreadRun(Sender: TIdThreadComponent);
var
FData: TStringList;
FMemory: TStringStream;
begin
if Client.Connected = False then Exit;
FData := TStringList.Create;
Client.IOHandler.Capture(FData, '', False);
FMemory := TStringStream.Create('',TEncoding.UTF8);
FMemory.WriteString(FData.Text);
CoInitialize(Nil);
QInsert.ParamByName('XMLData').LoadFromStream(FMemory,ftBlob);
QInsert.Execute;
CoUninitialize;
FMemory.Free;
FData.Free;
end;
如您所见,我正在将 XML 数据插入本地 table,这完成得非常快,15ms ~ 60ms,具体取决于 XML 的大小,但是越来越数据真的很蛋疼
Capture()
在循环中调用 ReadLn()
直到读取指定的终止符行(在您的示例中为空行)。
您需要知道每个 XML 文档的结束位置和下一个文档的开始位置。如果每个文档之间没有明显的标记(一个空行就可以,只要每个 XML 文档在空行之前以换行符结尾,并且 [=36 内部不包含任何空行=]), 那么你唯一剩下的选择就是阅读第一个文档的开始标签,继续阅读直到找到相应的结束标签,然后阅读下一个文档的开始标签并等待它的结束标签,依此类推。
在这种情况下,您最好使用支持推送模型的 XML 解析器来处理该问题。您只需从套接字中读取原始字节并将它们按原样推送到解析器中,让它在遇到每个顶级文档元素的开始和结束时告诉您。您可以缓冲解析器在这两个事件之间为您提供的任何数据,使用开始事件 clear/prepare 缓冲区和结束事件 finalize/flush 数据库缓冲区。
如果您不 willing/able 使用预先存在的 XML 库,则必须编写自己的解析器。
更新: 您展示的示例 Capture()
的 ADelim
参数设置为 ''
,只有 XML 文档由 LF LF
或 CR LF CR LF
序列分隔。在这种情况下,您可以将该定界符用作 ReadLn()
的 ATerminator
参数并让它一次读取 1 个整个文档,而不是完全使用 Capture()
:
procedure TfrmMain.ThreadBeforeRun(Sender: TIdThreadComponent);
begin
CoInitialize(Nil);
end;
procedure TfrmMain.ThreadAfterRun(Sender: TIdThreadComponent);
begin
CoUninitialize;
end;
procedure TfrmMain.ThreadRun(Sender: TIdThreadComponent);
var
Xml: String;
FMemory: TStringStream;
begin
// read until a 'CR LF CR LF' sequence is reached...
Xml := TrimRight(Client.IOHandler.ReadLn(CR+LF+CR+LF, IndyTextEncoding_UTF8));
// alternatively:
// Xml := Client.IOHandler.WaitFor(CR+LF+CR+LF, True, False, IndyTextEncoding_UTF8);
FMemory := TStringStream.Create(Xml, TEncoding.UTF8);
try
QInsert.ParamByName('XMLData').LoadFromStream(FMemory, ftBlob);
finally
FMemory.Free;
end;
QInsert.Execute;
end;