【问题标题】:TDictionary save to fileTDictionary 保存到文件
【发布时间】:2013-01-02 02:36:27
【问题描述】:

我有很多文件(大约 160 000 个),我需要有关文件中单个单词位置的信息(全文)。所以我像这样使用字典:

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

现在我知道 WORD1 在 FILE1、FILE3 和 FILE100 以及每个文件 , 等中的位置。 我可以填充它,我可以使用它——它非常快。但我不知道如何有效地将字典存储到文件中。

编辑:有效 - 我的意思是文件的快速和小尺寸

【问题讨论】:

  • 你所说的“有效”或“有效”是什么意思?小文件?快速加载整个文件?在不加载文件的情况下快速搜索单词?目前看起来最严格的方法是使用 TMemIniFile。
  • 使用 JSON 发射器转储它,并使用 JSON 解析器加载它
  • TIntegerDynArray - 最好使用TArray&lt;Integer&gt;,因为无论如何你都使用泛型。见stackoverflow.com/questions/11029353stackoverflow.com/questions/14383093
  • @David SuperObject 或 mORMot 的 JSON 是否与泛型一起“开箱即用”?特别是在加载时创建对象?并且使用 DBX JSON 几乎不会是最快且没有错误的
  • 另一种方法是使用嵌入式 SQL,如 NexusDB 或 SQLite - 通过规范化消除上述冗余,但会引入它们自己的开销。

标签: delphi full-text-search save tdictionary


【解决方案1】:

您可以使用 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,不,不是! fmInput 是用于旧文本文件的常量,并且已经存在了很长时间。它不能用于 TFileStream.Create。
  • 确实,恰恰相反。使用 fmInput 不是一个好主意......撤回我的评论。
猜你喜欢
  • 1970-01-01
  • 2017-11-18
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
相关资源
最近更新 更多