2009-08-30 44 views
6

我正在使用Delphi7(非unicode VCL),我需要在TFileStream中存储很多WideStrings。我不能使用TStringStream,因为(宽)字符串与二进制数据混合在一起,预计格式会加速加载和写入数据......但我相信目前我加载/写入字符串的方式可能是我的代码的瓶颈...(宽)字符串 - 存储在TFileStream,Delphi 7.什么是最快的方式?

当前我正在写一个字符串的长度,然后写入字符char ... 加载时,首先我加载长度,然后通过字符加载char ...

那么,什么是最快的方式来保存和加载WideString到TFileStream?

在此先感谢

+0

更改您的代码的特定区域,因为您* beli前夕*这可能是瓶颈可能是一个巨大的浪费时间。你应该先测量一下,有很多工具可以帮助你,有些是免费的,有些是商业的。首先尝试以下链接:http://stackoverflow.com/questions/291631/profiler-and-memory-analysis-tools-for-delphi和http://stackoverflow.com/questions/368938/delphi-profiling-tools – mghie 2009-08-30 15:45:22

+0

谢谢,但我使用QueryPerformanceCounter来检测;)无论如何,这是确定的瓶颈,由char读char非常慢......所有其他操作都只是保存一些简短的二进制数据。 – migajek 2009-08-30 17:48:31

+0

啊,好的。我只是对你使用“相信”和“可能”这两个词做出了反应,对于这个讲道感到抱歉;-) – mghie 2009-08-31 13:59:34

回答

6

而不是读取和写一次一个字符,同时读取和写入他们都:

procedure WriteWideString(const ws: WideString; stream: TStream); 
var 
    nChars: LongInt; 
begin 
    nChars := Length(ws); 
    stream.WriteBuffer(nChars, SizeOf(nChars); 
    if nChars > 0 then 
    stream.WriteBuffer(ws[1], nChars * SizeOf(ws[1])); 
end; 

function ReadWideString(stream: TStream): WideString; 
var 
    nChars: LongInt; 
begin 
    stream.ReadBuffer(nChars, SizeOf(nChars)); 
    SetLength(Result, nChars); 
    if nChars > 0 then 
    stream.ReadBuffer(Result[1], nChars * SizeOf(Result[1])); 
end; 

现在,在技术上,因为WideString是Windows BSTR,它可以包含一个奇数字节数。 Length函数读取字节数并除以2,所以有可能(尽管不太可能)上面的代码将切断最后一个字节。你可以使用此代码来代替:

procedure WriteWideString(const ws: WideString; stream: TStream); 
var 
    nBytes: LongInt; 
begin 
    nBytes := SysStringByteLen(Pointer(ws)); 
    stream.WriteBuffer(nBytes, SizeOf(nBytes)); 
    if nBytes > 0 then 
    stream.WriteBuffer(Pointer(ws)^, nBytes); 
end; 

function ReadWideString(stream: TStream): WideString; 
var 
    nBytes: LongInt; 
    buffer: PAnsiChar; 
begin 
    stream.ReadBuffer(nBytes, SizeOf(nBytes)); 
    if nBytes > 0 then begin 
    GetMem(buffer, nBytes); 
    try 
     stream.ReadBuffer(buffer^, nBytes); 
     Result := SysAllocStringByteLen(buffer, nBytes) 
    finally 
     FreeMem(buffer); 
    end; 
    end else 
    Result := ''; 
end; 

通过Mghie's answer启发,已经取代了我的ReadWrite电话与ReadBufferWriteBuffer。如果后者无法读取或写入所请求的字节数,则会引发异常。

+0

你的第二个'WriteWideString()'版本不会编译(缺少向PWideChar的类型转换,缺少paren),但更重要的是它对于空字符串失败。你的第二个'ReadWideString()'也应该检查长度为0,并在这种情况下简单地返回一个空字符串。 – mghie 2009-08-31 13:54:09

+0

我看不出没有理由它不适用于空字符串; 'SysStringByteLen'为空指针返回零。输入到'PWideChar'的要求是因为'SysStringByteLen'被错误地声明为'PWideChar'而不是'WideString',或者'BSTR'被错误地声明为'PWideChar'而不是'WideString'。尽管如此,我已经解决了这个问题并解决了您的其他问题。谢谢。 – 2009-08-31 16:36:19

+0

我*确实*看到它可能会因只有一个字节的字符串而失败的原因。启用范围检查后,表达式'ws [1]'在这种情况下应该失败。 (Delphi QC bug 9425和Free Pascal bug 0010013会影响它是否在任何特定版本上失败。) – 2009-08-31 16:43:55

6

没有什么特别之处宽字符串,阅读和他们尽可能快地写你需要一气呵成读写尽可能:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    Str: TStream; 
    W, W2: WideString; 
    L: integer; 
begin 
    W := 'foo bar baz'; 

    Str := TFileStream.Create('test.bin', fmCreate); 
    try 
    // write WideString 
    L := Length(W); 
    Str.WriteBuffer(L, SizeOf(integer)); 
    if L > 0 then 
     Str.WriteBuffer(W[1], L * SizeOf(WideChar)); 

    Str.Seek(0, soFromBeginning); 
    // read back WideString 
    Str.ReadBuffer(L, SizeOf(integer)); 
    if L > 0 then begin 
     SetLength(W2, L); 
     Str.ReadBuffer(W2[1], L * SizeOf(WideChar)); 
    end else 
     W2 := ''; 
    Assert(W = W2); 
    finally 
    Str.Free; 
    end; 
end; 
2

WideStrings包含WideChar的的“字符串” ,每个使用2个字节。如果您想要将UTF-16(WideStrings内部使用的)字符串存储在文件中,并且能够在其他程序(如记事本)中使用此文件,则需要首先编写byte order mark#$FEFF

如果你知道这一点,写作可以是这样的:

Stream1.Write(WideString1[1],Length(WideString)*2); //2=SizeOf(WideChar) 

阅读可以是这样的:

Stream1.Read(WideChar1,2);//assert returned 2 and WideChar1=#$FEFF 
SetLength(WideString1,(Stream1.Size div 2)-1); 
Stream1.Read(WideString1[1],(Stream1.Size div 2)-1); 
+1

他说他想存储大量的字符串,它们将与二进制数据混合在一起,并且它们将以它们的长度为前缀。 – 2009-08-30 16:38:53

+0

无条件访问空字符串的第一个元素的代码将导致访问冲突 – mghie 2009-08-31 13:58:17

1

您还可以使用TFastFileStream读取数据或字符串,我粘贴单位http://pastebin.com/m6ecdc8c2和下面的示例:

program Project36; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, Classes, 
    FastStream in 'FastStream.pas'; 

const 
    WideNull: WideChar = #0; 

procedure WriteWideStringToStream(Stream: TFileStream; var Data: WideString); 
var 
    len: Word; 
begin 
    len := Length(Data); 
    // Write WideString length 
    Stream.Write(len, SizeOf(len)); 
    if (len > 0) then 
    begin 
    // Write WideString 
    Stream.Write(Data[1], len * SizeOf(WideChar)); 
    end; 
    // Write null termination 
    Stream.Write(WideNull, SizeOf(WideNull)); 
end; 

procedure CreateTestFile; 
var 
    Stream: TFileStream; 
    MyString: WideString; 
begin 
    Stream := TFileStream.Create('test.bin', fmCreate); 
    try 
    MyString := 'Hello World!'; 
    WriteWideStringToStream(Stream, MyString); 

    MyString := 'Speed is Delphi!'; 
    WriteWideStringToStream(Stream, MyString); 
    finally 
    Stream.Free; 
    end; 
end; 

function ReadWideStringFromStream(Stream: TFastFileStream): WideString; 
var 
    len: Word; 
begin 
    // Read length of WideString 
    Stream.Read(len, SizeOf(len)); 
    // Read WideString 
    Result := PWideChar(Cardinal(Stream.Memory) + Stream.Position); 
    // Update position and skip null termination 
    Stream.Position := Stream.Position + (len * SizeOf(WideChar)) + SizeOf(WideNull); 
end; 

procedure ReadTestFile; 
var 
    Stream: TFastFileStream; 

    my_wide_string: WideString; 
begin 
    Stream := TFastFileStream.Create('test.bin'); 
    try 
    Stream.Position := 0; 
    // Read WideString 
    my_wide_string := ReadWideStringFromStream(Stream); 
    WriteLn(my_wide_string); 
    // Read another WideString 
    my_wide_string := ReadWideStringFromStream(Stream); 
    WriteLn(my_wide_string); 
    finally 
    Stream.Free; 
    end; 
end; 

begin 
    CreateTestFile; 
    ReadTestFile; 
    ReadLn; 
end. 
+2

注意:如果要读取的字符串包含该代码,则该代码将不起作用任何空字符 – 2009-08-30 19:30:49

+0

无条件地访问空字符串的第一个元素的代码将导致访问违规 – mghie 2009-08-31 13:57:40

+0

感谢mghie,代码是固定的。 – pani 2009-08-31 15:23:27

相关问题