Sign in Join
Wiki copytemporary

File Handling In Pascal/zh CN

English · Español · Français · Suomi · Русский · العربية · 中文(中国) · 中文(台灣) · 日本語

大多数程序员都需要知道如何操作文件。文件可以用来保存用户设置、错误日志等等。在这里我将教你如何操作文本文件。

旧程序风格

使用标准Pascal文件(非面向对象),你可以使用一个文本文件类型,它允许你将字符串写入到文件或创建自己的文件类型。

pascal
...
type
 TIntegerFile = file of Integer; // 允许你将整数写入文件
 TPCharFile = file of PChar; // PChars写入到文件
 TStringFile = file of String; // 字符串写入到文件
...

If we only did TStringFile = File, then it would be impossible to write anything into it! Also, you cannot write integers directly into TStringFile, because it is a file of strings. Better use the filetype TextFile for writing values of different types.

(如果我们只做了TStringFile = File,那么它不能被写入任何东西!此外,你不能将整数写入到TStringFile。因为它是一个字符串文件。更好的使用文件类型来存储不同类型的值。)

IO

IO is the file handling thingy for Pascal.(IO是Pascal文件处理的),它告诉编译器如何处理IO错误:抛出一个异常或将结果存储到IOResult变量。 因为它是一个编译器指令,所以:

pascal
{$I-} // 关闭检查。将所有的错误存入IOResult变量
{$I+} // 把它重新打开,将导致EInOutError异常

通过禁用/关闭 $I, 文件操作结果进入IOResult变量。这是一个基数数字类型. 所以,如果你想写IOResult,你必须使用IntToStr功能。不同的数字代表不同的错误。所以你可能要检查文档中不同的错误: 1.

文件程序

这些文件处理过程和函数位于系统单元。请参阅FPC文档了解更多信息: 系统单元参考.

示例

一个完整的处理文本文件的示例:

pascal
program FileTest;

{$mode objfpc} // 不要忘了这个

uses
 Sysutils;

var
 FileVar: TextFile;

begin
  WriteLn('File Test');
  AssignFile(FileVar, 'Test.txt'); // 你不需要输出 .txt 但现在需要
  {$I+} // 使用异常处理
  try  
    Rewrite(FileVar);  // 创建文件
    Writeln(FileVar,'Hello');
    // Use CloseFile rather than Close as Close is used in other units as well
    CloseFile(FileVar);
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Program finished. Press enter to stop.');  
  ReadLn;
end.

任意编辑器打开这个文件,你将看到'''Hello'! 你可以设置Test.txt文件为只读,再运行程序。这时程序会抛出异常,显示错误消息。

Note: 我们使用异常处理({$I+}),它是对多个文件操作及处理错误时的一个简单方法。你也可以使用{$I-},但你必须在每次操作后检查但你必须检查IOResult,并修改你的下一步操作。

以下是如何追加/添加到文件的示例:

pascal
program EditFile;

{$mode objfpc}

uses
 Sysutils;

var
 File1: TextFile;
 
begin
  WriteLn('Append file');
  {$I+}
  try
    AssignFile(File1, 'File.txt');
    { 
    我这里测试时,原版示例不能执行报错 Error: Wrong number of parameters specified for call to "Append"
    在Lazarus 1.2.2,FPC2.6.4上
    }
    // Append(File1, 'adding some text...'); 
    { 修改后的程序 }
    Append(File1);
    Writeln(FileVar,'adding some text...'); 
    Writeln
    CloseFile(File1);
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Program finished. Press enter to stop.');  
  Readln;
end.

读取文件:

pascal
program ReadFile;

{$mode objfpc}

uses
 Sysutils;

var
 File1: TextFile;
 Str: String;

begin
  Writeln('File Reading:');
  AssignFile(File1, 'File.txt');
  {$I+}
  try
    Reset(File1);
    repeat
      Readln(File1, Str); // 从文件中读取一行
      Writeln(Str); // 显示这行
    until(EOF(File1)); // EOF(文件结束)程序将继续读取直到文件结尾。
    CloseFile(File1);
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Program finished. Press enter to stop.');  
  Readln;
end.

可以做一些文件处理使用字符而不是字符串。这使得它看起来酷:D。

对象风格

除了上面提到的旧式文件处理例程,在新系统更高的抽象层次里使用流(数据流)的概念,这意味着作为一个程序员在处理文件时执行更少的步骤。

此外,大部分字符串处理类可以加载(保存)内容从(到)文件。这些方法通常是SaveToFile和LoadFromFile。很多其他对象(像Lazarus网格)也有类似的功能,包括Lazarus数据集(DBExport);it pays to look through the documentation/source code before trying to roll your own import/export routines.

二进制文件

为操作文件应该使用直接使用TFileStream。这个类封装了系统程序,FileCreate,FileRead,FileWrite,FileSeek和FileClose它在FileUtil单元。

IO 例程

在下面的例子中,注意我们是如何封装处理文件在try... finally块中。这可以确保文件流对象总是释放(在finally... 部分),即使有文件访问(或其他)错误。

pascal
var
  Buffer: array[0..9999] of Byte;
begin
  with TFileStream.Create('SomeFile.bin', fmCreate) do 
  try
    Seek('Hello');
    Write(Buffer, SizeOf(Buffer));
  finally
    Free;
  end;
end;

你可以加载整个文件到内存中,如果文件大小大于系统可用内存,你的操作系统会开始使用页面/交换文件,making the exercise useless 从性能角度来看。

pascal
begin
  with TMemoryStream.Create do 
  try
    LoadFromFile('SomeFile.bin');
    Seek(0, soEnd);
    Write(Ord('A'), 1);
    SaveToFile('SomeFile.bin');
  finally
    Free;
  end;
end;

With larger files of many Gb, you may want to read in buffers of, say, 4096 bytes (you're advised to use a multiple of the filesytem cluster or block size) and do something with the data of each buffer read.

(你可能需要读取许多Gb大文件,比方说4096 字节(建议你使用集群文件系统或块大小的整数倍)并从缓冲区里读取数据。)

pascal
var
  TotalBytesRead, BytesRead : Int64;
  Buffer : array [0..4095] of byte;  // 或 array [0..4095] 为 char
  FileStream : TFileStream;

try
  FileStream := TFileStream.Create;
  FileStream.Position := 0;  // 确保在文件开始位置
  while TotalBytesRead <= FileStream.Size do  // 当总读取大小 <= 文件大小
  begin
    BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // 读取 4096 字节数据
    inc(TotalBytesRead, BytesRead);                       // 增加 TotalByteRead 缓冲区的大小,也就是说 4096 字节
    // 做一些与缓冲数据相关的操作
  end;

复制文件

与上述,我们可以实现一个简单的FileCopy功能(FreePascal has none in its RTL although Lazarus has one) - adjust as needed for bigger files etc:(FreePascal现在没有Lzarus中的RTL中有一个)大文件等需要做些调整:

pascal
function FileCopy(Source, Target: string): boolean;
// 复制文件 source 到 target;覆盖目标文件
// 在内存中缓存整个文件
// 成功返回 true,失败返回false
var
  MemBuffer: TMemoryStream;
begin
  result:=false;
  MemBuffer:=TMemoryStream.Create;
  try
    try
      MemBuffer.LoadFromFile(Source);
      MemBuffer.Position:=0;
      MemBuffer.SaveToFile(Target); //可能是源文件相同
      result:=true;
    except
      result:=false; //忽略异常,转换为错误代码
    end;
  finally
    MemBuffer.Free;
  end;
end;

文本文件

一般情况下,对于文本文件可以使用 TStringList 类将整个文件加载到内存中,并可以对行进行简单存取。当然,你也可以保存StringList到文件:

pascal
begin
  with TStringList.Create do 
  try
    Add('Hello');
    SaveToFile('SomeFile.txt');
  finally
    Free;
  end;
end;

为了写单个字符串到流可能需要使用以下过程:

pascal
procedure SaveStringToPath(theString, filePath: String);
var
  textFile: TFileStream = nil;
  textLength: integer;
  stringBuffer: ^String;
begin
  textLength := length(theString);
  try
    textFile := TFileStream.Create(filePath, fmOpenWrite or fmCreate);
    { write string to stream while avoiding to write the initial length }
    { 写字符串到流,同时避免写入超过最初长度 }
    stringBuffer := @theString + 1;
    textFile.WriteBuffer(stringBuffer^, textLength);
  finally
    if textFile <> nil then textFile.Free;
  end;
end;

参见


From the Free Pascal wiki, page "File Handling In Pascal/zh CN", as it stood on 2020-02-14 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Edited on wiki.freepascal.org by 3 people, 2014–2020

From the official wiki's backup of 26 May 2024: who edited this page there, and when. Edits made there after that date were not recorded, and the earlier versions are not shown here; the main page says why.

WhenWhoWhat changed
WangyouworldFile Handling In Pascal/zh CN
Wangyouworldsyntaxhighlight (minor)
FTurtle
TrevFixed syntax highlighting; deleted category included in page template (minor)

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.