【问题标题】:How to read TZipFile.FileComment as a string?如何将 TZipFile.FileComment 读取为字符串?
【发布时间】:2014-01-24 17:19:48
【问题描述】:

当您尝试读取 TZipFile.FileComment 时,编译器会报告这将无法编译: [dcc64 错误] Unit1.pas(451): E2010 不兼容的类型:'string' 和 'System.TArray'

var
  iFileComment: string;
...
iFileComment := iZipFile.FileInfo[i].FileComment;

那么...您如何阅读 TZipFile 注释并将其分配给字符串?这看起来很奇怪,因为我可以成功编写 zipfile 注释如下:

iZipFile.FileComment[ListView1.ItemIndex] := FileComment1.Text;

如果您尝试阅读评论:

iFileComment := iZipFile.FileComment[i];

zip 文件中的每个文件都有相同的注释,即使实际上只有一个文件有文件注释。

cmets 是这样添加的:

procedure TForm1.SetFileComment1Click(Sender: TObject); var  
iZipFile: TZipFile;   iListItem: TlistItem; 
begin   
  if ListView1.ItemIndex <> -1 then   
  begin
     iZipFile := TZipFile.Create;
     try
       { Open zip file for writing }
       iZipFile.Open(ZipFilename1.Text, zmReadWrite);
       iZipFile.FileComment[ListView1.ItemIndex] := FileComment1.Text;
       { Close the zip file }
       iZipFile.Close;
       { Update the listview }
       ListView1.Items.BeginUpdate;
       try
         iListItem := ListView1.Items.Item[ListView1.ItemIndex];
         iListItem.SubItems[5] := FileComment1.Text;
       finally
         ListView1.Items.EndUpdate;
       end;
     finally
       iZipFile.Free;
     end;   
  end   
  else   
  begin
    MessageBox(0, 'A filename is not selected. Please select a filename.',
       'Warning', MB_ICONINFORMATION or MB_OK);
  end; 
end;

编辑

我现在按照建议使用 iZipFile.FileComment[Index] 设置和写入文件cmets。 当我设置一个文件的注释并在工作的 zip 应用程序中打开 zip 文件时,只有我设置的单个文件与注释一起出现......在这个 TZipFile 项目中,当我加载相同的 zip 文件时,每个文件都有相同的注释,但我看不出为什么。除了 filecomment 字段之外,所有其他字段都按照您的预期运行。

这是打开 zip 文件的代码:

procedure TForm1.Open1Click(Sender: TObject);
{ Open zip file. }
var
  i: integer;
  iZipFile: TZipFile;
  iFilename: string;
  iDateTime: TDateTime;
  iCompressedSize: cardinal;
  iUnCompressedSize: cardinal;
  iCRC32: cardinal;
  iCompressionMethod: word;
  iFileComment: string;
  iListItem: TlistItem;
begin
  if OpenDialog1.Execute then
  begin
    if FileExists(OpenDialog1.FileName) then
    begin
      iZipFile := TZipFile.Create;
      try
        ListView1.Items.Clear;
        ZipFilename1.Text := OpenDialog1.FileName;
        try
          { Open zip file for reading }
          iZipFile.Open(ZipFilename1.Text, zmReadWrite);
          for i := 0 to iZipFile.FileCount - 1 do
          begin
            iFilename := iZipFile.FileNames[i];
            iListItem := ListView1.Items.Add;
            iListItem.Caption := iFilename;
            iDateTime := FileDateToDateTime
              (iZipFile.FileInfo[i].ModifiedDateTime);
            iListItem.SubItems.Add(DateTimeToStr(iDateTime)); { 0 }
            iCompressedSize := iZipFile.FileInfo[i].CompressedSize;
            iListItem.SubItems.Add(FormatByteSize(iCompressedSize)); { 1 }
            iUnCompressedSize := iZipFile.FileInfo[i].UncompressedSize;
            iListItem.SubItems.Add(FormatByteSize(iUnCompressedSize)); { 2 }
            iCRC32 := iZipFile.FileInfo[i].CRC32;
            iListItem.SubItems.Add(IntToStr(iCRC32)); { 3 }
            iCompressionMethod := iZipFile.FileInfo[i].CompressionMethod;
            iListItem.SubItems.Add
              (ZipCompressionToStr(iCompressionMethod)); { 4 }
            iFileComment := iZipFile.FileComment[i];
            iListItem.SubItems.Add(iFileComment); { 5 }
          end;
          { Close the zip file }
          iZipFile.Close;
        except
          on E: Exception do
          begin
            ShowMessage(E.ClassName + #10#13 + E.Message);
          end;
        end;
      finally
        iZipFile.Free;
      end;
    end;
  end;
end;

【问题讨论】:

    标签: delphi delphi-xe4


    【解决方案1】:

    你读回它的方式与你写它的方式相同,相反:

    // Write the comment
    iZipFile.FileComment[Index] := 'This is a zip file comment';
    
    // Read it back
    sComment := iZipFile.FileComment[Index];
    WriteLn(sComment);             // Outputs "This is a zip file comment"
    

    Index 将匹配 zip 存档中单个文件的索引。换句话说,第一个文件位于索引 0,第二个位于索引 1,依此类推,最后一个位于索引 FileCount - 1

    这是一个工作示例。我创建了一个名为 Test.zip 的虚拟 zip 文件,其中包含一些我在此处的答案中使用过的旧 .png 文件(如下图所示):

    然后我使用下面的代码将 cmets 添加到每个文件并读回它们,将它们写入控制台:

    program Project1;
    
    {$APPTYPE CONSOLE}
    
    uses
      System.SysUtils, System.Zip;
    
    var
      Zip: TZipFile;
      i: Integer;
    begin
      Zip := TZipFile.Create;
      Zip.Open('D:\TempFiles\Test.zip', zmReadWrite);
      for i := 0 to Zip.FileCount - 1 do
        Zip.FileComment[i] := Format('This is file %d', [i]);
      Zip.Free;  // Flushes changes to disk automatically
    
      // Create a new instance, just to make it clear.
      Zip := TZipFile.Create;
      Zip.Open('D:\TempFiles\Test.zip', zmReadWrite);
      for i := 0 to Zip.FileCount - 1 do
        WriteLn(Zip.FileNames[i] + ':'#9 + Zip.FileComment[i]);
      Zip.Free;
      ReadLn;
    end.
    

    这是它产生的输出:

    这是它添加的 zip 文件 cmets,如 7Zip 所示:

    【讨论】:

    • 谢谢肯...我最后做了什么...但是 zip 文件中的每个文件都有相同的注释,即使实际上只有一个文件有文件注释。至少现在可以编译了
    • @Bill:查看我编辑的答案以获取演示的工作代码。
    • 感谢您的代码。我改变了我的做法,但文件注释字段存在问题,因为 zip 中的所有文件都加载了相同的注释,尽管为一个文件设置了一个文件注释。查看我的编辑,显示我如何打开 zip 文件...现在看来是问题所在。
    • @Bill:不知道该告诉你什么,除了创建一个新的测试应用程序,消除所有与列表视图无关的东西,并找出问题所在。我添加了单个文件 cmets 的图像(由上面的示例代码添加),如 7zip 的 GUI 中所示,这清楚地表明单个文件具有单个 cmets。
    • 谢谢肯...会做的。
    【解决方案2】:

    在 XE2 中,TZipHeader.FileComment 被声明为 RawByteString,可以直接分配给 String(使用类型转换以避免编译器警告):

    iFileComment := String(iZipFile.FileInfo[i].FileComment);
    

    但是,在 XE4 中,TZipHeader.FileComment 被声明为 TBytes。您不能直接将 TBytes 分配给 String(反之亦然),这会导致编译器错误。

    如果您查看 XE4 中TZipFile.GetFileComment() 的实现,它使用TZipFile.TBytesToString()TBytes 转换为StringTZipFile.TBytesToString() 使用 SysUtils.TEncoding.GetString() 进行实际转换,使用代码页 65001 或 437,具体取决于 TZipFile.UTF8Support 属性的值。

    获取/设置每个文件注释的正确方法是使用TZipFile.FileComments[] 属性,就像Ken 演示的那样。它不仅为您处理字符串字节转换,而且还在访问数据之前检查以确保 zip 已打开。

    【讨论】:

      【解决方案3】:

      很抱歉再次提出这个老问题,但没有一个答案真正解决了 zip 文件中的每个文件都有相同的注释 问题。

      原因是TZipFile.ReadCentralHeader 中的一个错误

      当一个 zip 文件打开时,ReadCentralHeader 将所有 zip 目录条目读入一个内部记录列表,但未能清除每个条目之间用于填充列表的临时记录。

      在文件注释ReadCentralHeader 的情况下,首先读取FileCommentLength 值,但如果该值为零,则它不会设置FileComment 值的值,因此保留前一个条目的值。

      一种可能的解决方法是在读取FileComment[] 属性时考虑FileCommentLength 值:

      Comment := Copy(ZipFile.FileComment[Index], 1, ZipFile.FileInfo[Index].FileCommentLength);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-24
        • 2013-02-13
        • 1970-01-01
        • 2012-03-28
        • 1970-01-01
        • 2015-09-08
        • 2020-05-16
        相关资源
        最近更新 更多