TDictionary保存到文件

4

我有很多文件(大约160,000个),需要获取这些文件(全文)中每个单词的位置信息。因此,我使用了以下此类字典:

WordDict : TDictionary<string, TDictionary<string, TIntegerDynArray>>;

现在我知道WORD1出现在FILE1,FILE3和FILE100中,并且在每个文件中的位置分别为<1,3,5>,<2,8,35>等。我可以填写它,使用它-非常快速。但我不知道如何有效地将字典存储到文件中。
编辑:有效地指快速地且文件尺寸较小。

你所说的"effectively"或"efficiently"是什么意思?是指文件小吗?整个文件加载快吗?搜索单词快而不必加载整个文件吗? 目前看来,最直接的方法似乎是使用TMemIniFile。 - Arioch 'The
使用JSON发射器进行转储,使用JSON解析器进行加载。 - David Heffernan
TIntegerDynArray - 最好使用 TArray<Integer>,因为您已经使用了泛型。请参见 https://dev59.com/6mgu5IYBdhLWcg3w2ahm 和 https://dev59.com/vWYq5IYBdhLWcg3wkRUo。 - Arioch 'The
@David,SuperObject或mORMot的JSON是否可以直接与泛型一起使用?特别是在加载时创建对象?而且使用DBX JSON几乎不可能是最快和无错误的。 - Arioch 'The
我还使用DiUCL或ZIP进行了文本存储的归档(如果Delphi中有PPMd实现并且依赖于7z包装器会过度臃肿,则不需要)。这样可以使加载速度更快,大小更小。最多的冗余来自于重复的文件名,而归档应该可以很好地处理它,就像对OpenOffice和MS-Office 2007+一样。Zip在解压缩时还会检查文件是否损坏。 - Arioch 'The
2
另一种方法是使用嵌入式SQL,如NexusDB或SQLite - 这将通过规范化消除上述冗余,但会引入它们自己的开销。 - Arioch 'The
1个回答

13
你可以使用 Delphi 的流系统来编写专有的流格式。如果大小很重要(与速度相反),你可以对流进行压缩。以下是一些代码:

您可以使用Delphi的流处理系统编写专有的流格式。 如果大小很重要(而不是速度),则可以对流进行压缩。以下是一些代码:

type
  TFilePos = TArray<Integer>;
  TFileDict = TDictionary<string, TFilePos>;
  TWordDict = class (TDictionary<string, TFileDict>)
  private
    procedure LoadFromStream(stream: TStream);
    procedure SaveToStream(stream: TStream);
  public
    procedure LoadFromZip(const AFileName: string);
    procedure LoadFromFile(const AFileName: string);
    procedure SaveToZip(const AFileName: string);
    procedure SaveToFile(const AFileName: string);
  end;

procedure TWordDict.LoadFromZip(const AFileName: string);
var
  stream: TStream;
  localHeader: TZipHeader;
  zipFile: TZipFile;
begin
  zipFile := TZipFile.Create;
  try
    zipFIle.Open(AFIleName, zmRead);
    zipFile.Read('worddict', stream, localHeader);
    try
      LoadFromStream(stream);
    finally
      stream.Free;
    end;
    zipFile.Close;
  finally
    zipFile.Free;
  end;
end;

procedure TWordDict.SaveToZip(const AFileName: string);
var
  stream: TStream;
  zipFile: TZipFile;
begin
  stream := TMemoryStream.Create;
  try
    SaveToStream(stream);
    stream.Position := 0;
    zipFile := TZipFile.Create;
    try
      zipFile.Open(AFileName, zmWrite);
      zipFile.Add(stream, 'worddict');
      zipFile.Close;
    finally
      zipFile.Free;
    end;
  finally
    stream.Free;
  end;
end;

procedure TWordDict.SaveToStream(stream: TStream);
var
  posi: System.Generics.Collections.TPair<string, TFilePos>;
  i: Integer;
  pair: System.Generics.Collections.TPair<string, TFileDict>;
  writer: TWriter;
begin
  writer := TWriter.Create(stream, 4096);
  try
    writer.WriteListBegin;
    for pair in Self do
    begin
      writer.WriteString(pair.Key);
      writer.WriteListBegin;
      for posi in pair.Value do
      begin
        writer.WriteString(posi.Key);
        writer.WriteInteger(Length(posi.Value));
        for i in posi.Value do
        begin
          writer.WriteInteger(i);
        end;
      end;
      writer.WriteListEnd;
    end;
    writer.WriteListEnd;
  finally
    writer.Free;
  end;
end;

procedure TWordDict.LoadFromStream(stream: TStream);
var
  sFiles: TFileDict;
  aPosi: TFilePos;
  size: Integer;
  i: Integer;
  sWord: string;
  reader: TReader;
  sFile: string;
begin
  Clear;
  reader := TReader.Create(stream, 1024);
  try
    reader.ReadListBegin;
    while not reader.EndOfList do
    begin
      sWord := reader.ReadString;
      sFiles := TFileDict.Create;
      reader.ReadListBegin;
      while not reader.EndOfList do
      begin
        sFile := reader.ReadString;
        size := reader.ReadInteger;
        SetLength(aPosi, size);
        for I := 0 to size - 1 do
        begin
          aPosi[I] := reader.ReadInteger;
        end;
        sFiles.Add(sFile, Copy(aPosi));
      end;
      reader.ReadListEnd;
      Add(sWord, sFiles);
    end;
    reader.ReadListEnd;
  finally
    reader.Free;
  end;
end;

procedure TWordDict.LoadFromFile(const AFileName: string);
var
  stream: TStream;
begin
  stream := TFileStream.Create(AFileName, fmOpenRead);
  try
    LoadFromStream(stream);
  finally
    stream.Free;
  end;
end;

procedure TWordDict.SaveToFile(const AFileName: string);
var
  stream: TStream;
begin
  stream := TFileStream.Create(AFileName, fmCreate);
  try
    SaveToStream(stream);
  finally
    stream.Free;
  end;
end;

fmOpenRead在Delphi Seattle中现已改为fmInput。 - kStarbe
1
@kStarbe,不是这样的!fmInput是用于旧文本文件的常量,已经存在很长时间了。它不能用于TFileStream.Create。 - Uwe Raabe
1
确实,相反的情况更为合适。使用fmInput并不是一个好主意... 撤回我的评论。 - kStarbe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接