(ワイド)文字列 - TFileStream、Delphi 7 に保存します。一番早い方法は何ですか?
-
20-09-2019 - |
質問
Delphi7 (非 Unicode VCL) を使用しているため、TFileStream 内に大量の WideString を保存する必要があります。(ワイド)文字列がバイナリデータと混合されているため、TStringStream を使用できません。データの読み込みと書き込みを高速化するために形式が投影されています...ただし、文字列のロード/書き込みの現在の方法がコードのボトルネックになっている可能性があると思います...
現在、文字列の長さを書き込んでから、文字ごとに書いています...ロード中、最初に長さをロードし、次に文字ごとにロードします...
では、 WideString を TFileStream に保存してロードする最も速い方法は何でしょうか?
前もって感謝します
解決
一度に 1 文字ずつ読み書きするのではなく、すべてを一度に読み書きします。
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;
に触発された ムギーの答え, 、私の代わりになりました Read
そして Write
との通話 ReadBuffer
そして WriteBuffer
. 。後者では、要求されたバイト数を読み書きできない場合に例外が発生します。
他のヒント
ワイド文字列について何も特別な、読んで、あなたが一度にできるだけ多くを読み書きする必要があり、できるだけ速くそれを書くためにはありません。
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;
WideStrings には、それぞれ 2 バイトを使用する WideChar の「文字列」が含まれます。UTF-16 ( WideStrings が内部で使用する) 文字列をファイルに保存し、このファイルをメモ帳などの他のプログラムで使用できるようにしたい場合は、次のように記述する必要があります。 バイトオーダーマーク 初め: #$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);
また、データや文字列を読み取るため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.