Difference between revisions of "File Handling In Pascal/zh CN"
Wangyouworld (talk | contribs) |
Wangyouworld (talk | contribs) |
||
Line 7: | Line 7: | ||
使用标准Pascal文件(非面向对象),你可以使用一个文本文件类型,它允许你将字符串写入到文件或创建自己的文件类型。 | 使用标准Pascal文件(非面向对象),你可以使用一个文本文件类型,它允许你将字符串写入到文件或创建自己的文件类型。 | ||
− | < | + | <syntaxhighlight>... |
type | type | ||
TIntegerFile = file of Integer; // 允许你将整数写入文件 | TIntegerFile = file of Integer; // 允许你将整数写入文件 | ||
TPCharFile = file of PChar; // PChars写入到文件 | TPCharFile = file of PChar; // PChars写入到文件 | ||
TStringFile = file of String; // 字符串写入到文件 | TStringFile = file of String; // 字符串写入到文件 | ||
− | ...</ | + | ...</syntaxhighlight> |
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. | 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. | ||
Line 22: | Line 22: | ||
IO is the file handling thingy for Pascal.(<strike>IO是Pascal文件处理的</strike>),它告诉编译器如何处理IO错误:抛出一个异常或将结果存储到IOResult变量。 | IO is the file handling thingy for Pascal.(<strike>IO是Pascal文件处理的</strike>),它告诉编译器如何处理IO错误:抛出一个异常或将结果存储到IOResult变量。 | ||
因为它是一个编译器指令,所以: | 因为它是一个编译器指令,所以: | ||
− | < | + | <syntaxhighlight>{$I-} // 关闭检查。将所有的错误存入IOResult变量 |
{$I+} // 把它重新打开,将导致EInOutError异常 | {$I+} // 把它重新打开,将导致EInOutError异常 | ||
− | </ | + | </syntaxhighlight> |
通过禁用/关闭 $I, 文件操作结果进入IOResult变量。这是一个[[Cardinal|基数数字类型]]. 所以,如果你想写IOResult,你必须使用IntToStr功能。不同的数字代表不同的错误。所以你可能要检查文档中不同的错误: [http://www.freepascal.org/docs-html/rtl/system/ioresult.html]. | 通过禁用/关闭 $I, 文件操作结果进入IOResult变量。这是一个[[Cardinal|基数数字类型]]. 所以,如果你想写IOResult,你必须使用IntToStr功能。不同的数字代表不同的错误。所以你可能要检查文档中不同的错误: [http://www.freepascal.org/docs-html/rtl/system/ioresult.html]. | ||
Line 32: | Line 32: | ||
这些文件处理过程和函数位于系统单元。请参阅FPC文档了解更多信息: [http://www.freepascal.org/docs-html/rtl/system/index-5.html 系统单元参考]. | 这些文件处理过程和函数位于系统单元。请参阅FPC文档了解更多信息: [http://www.freepascal.org/docs-html/rtl/system/index-5.html 系统单元参考]. | ||
− | * '''Assign''' - | + | * '''AssignFile'''(或者旧的'''Assign''') - 将文件名赋给变量名 |
* '''Append''' - 以附加的方式打开已有的文件 | * '''Append''' - 以附加的方式打开已有的文件 | ||
* '''BlockRead''' - 读一个或多个记录到变量中 | * '''BlockRead''' - 读一个或多个记录到变量中 | ||
* '''BlockWrite''' - 从变量中写一个或多个记录 | * '''BlockWrite''' - 从变量中写一个或多个记录 | ||
− | * '''Close''' - 关闭打开的文件 | + | * '''CloseFile'''(或者旧的'''Close''') - 关闭打开的文件 |
* '''EOF''' - 测试文件是否到文件尾 | * '''EOF''' - 测试文件是否到文件尾 | ||
* '''Erase''' - 删除文件 | * '''Erase''' - 删除文件 | ||
Line 59: | Line 59: | ||
一个完整的处理文本文件的示例: | 一个完整的处理文本文件的示例: | ||
− | < | + | <syntaxhighlight>program FileTest; |
{$mode objfpc} // 不要忘了这个 | {$mode objfpc} // 不要忘了这个 | ||
Line 76: | Line 76: | ||
Rewrite(FileVar); // 创建文件 | Rewrite(FileVar); // 创建文件 | ||
Writeln(FileVar,'Hello'); | Writeln(FileVar,'Hello'); | ||
+ | // Use CloseFile rather than Close as Close is used in other units as well | ||
CloseFile(FileVar); | CloseFile(FileVar); | ||
except | except | ||
Line 85: | Line 86: | ||
WriteLn('Program finished. Press enter to stop.'); | WriteLn('Program finished. Press enter to stop.'); | ||
ReadLn; | ReadLn; | ||
− | end.</ | + | end.</syntaxhighlight> |
任意编辑器打开这个文件,你将看到'''Hello'! | 任意编辑器打开这个文件,你将看到'''Hello'! | ||
Line 95: | Line 96: | ||
以下是如何追加/添加到文件的示例: | 以下是如何追加/添加到文件的示例: | ||
− | < | + | <syntaxhighlight>program EditFile; |
Line 129: | Line 130: | ||
WriteLn('Program finished. Press enter to stop.'); | WriteLn('Program finished. Press enter to stop.'); | ||
Readln; | Readln; | ||
− | end.</ | + | end.</syntaxhighlight> |
读取文件: | 读取文件: | ||
− | < | + | <syntaxhighlight>program ReadFile; |
Line 164: | Line 165: | ||
WriteLn('Program finished. Press enter to stop.'); | WriteLn('Program finished. Press enter to stop.'); | ||
Readln; | Readln; | ||
− | end.</ | + | end.</syntaxhighlight> |
可以做一些文件处理使用字符而不是字符串。这使得它看起来酷:D。 | 可以做一些文件处理使用字符而不是字符串。这使得它看起来酷:D。 | ||
Line 182: | Line 183: | ||
在下面的例子中,注意我们是如何封装处理文件在try... finally块中。这可以确保文件流对象总是释放(在finally... 部分),即使有文件访问(或其他)错误。 | 在下面的例子中,注意我们是如何封装处理文件在try... finally块中。这可以确保文件流对象总是释放(在finally... 部分),即使有文件访问(或其他)错误。 | ||
− | < | + | <syntaxhighlight>var |
Buffer: array[0..9999] of Byte; | Buffer: array[0..9999] of Byte; | ||
begin | begin | ||
Line 192: | Line 193: | ||
Free; | Free; | ||
end; | end; | ||
− | end;</ | + | end;</syntaxhighlight> |
你可以加载整个文件到内存中,如果文件大小大于系统可用内存,你的操作系统会开始使用页面/交换文件,making the exercise useless 从性能角度来看。 | 你可以加载整个文件到内存中,如果文件大小大于系统可用内存,你的操作系统会开始使用页面/交换文件,making the exercise useless 从性能角度来看。 | ||
− | < | + | <syntaxhighlight>begin |
with TMemoryStream.Create do | with TMemoryStream.Create do | ||
try | try | ||
Line 206: | Line 207: | ||
Free; | Free; | ||
end; | end; | ||
− | end;</ | + | end;</syntaxhighlight> |
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. | 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. | ||
Line 212: | Line 213: | ||
(<strike>你可能需要读取许多Gb大文件,比方说4096 字节(建议你使用集群文件系统或块大小的整数倍)并从缓冲区里读取数据。</strike>) | (<strike>你可能需要读取许多Gb大文件,比方说4096 字节(建议你使用集群文件系统或块大小的整数倍)并从缓冲区里读取数据。</strike>) | ||
− | < | + | <syntaxhighlight> |
var | var | ||
TotalBytesRead, BytesRead : Int64; | TotalBytesRead, BytesRead : Int64; | ||
Line 227: | Line 228: | ||
// 做一些与缓冲数据相关的操作 | // 做一些与缓冲数据相关的操作 | ||
end; | end; | ||
− | </ | + | </syntaxhighlight> |
=== 复制文件 === | === 复制文件 === | ||
Line 261: | Line 262: | ||
一般情况下,对于文本文件可以使用 TStringList 类将整个文件加载到内存中,并可以对行进行简单存取。当然,你也可以保存StringList到文件: | 一般情况下,对于文本文件可以使用 TStringList 类将整个文件加载到内存中,并可以对行进行简单存取。当然,你也可以保存StringList到文件: | ||
− | < | + | <syntaxhighlight>begin |
with TStringList.Create do | with TStringList.Create do | ||
try | try | ||
Line 269: | Line 270: | ||
Free; | Free; | ||
end; | end; | ||
− | end;</ | + | end;</syntaxhighlight> |
为了写单个字符串到流可能需要使用以下过程: | 为了写单个字符串到流可能需要使用以下过程: |
Revision as of 03:28, 30 July 2014
│
العربية (ar) │
English (en) │
español (es) │
suomi (fi) │
français (fr) │
日本語 (ja) │
русский (ru) │
中文(中国大陆) (zh_CN) │
中文(台灣) (zh_TW) │
大多数程序员都需要知道如何操作文件。文件可以用来保存用户设置、错误日志等等。在这里我将教你如何操作文本文件。
旧程序风格
使用标准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变量。
因为它是一个编译器指令,所以:
{$I-} // 关闭检查。将所有的错误存入IOResult变量
{$I+} // 把它重新打开,将导致EInOutError异常
通过禁用/关闭 $I, 文件操作结果进入IOResult变量。这是一个基数数字类型. 所以,如果你想写IOResult,你必须使用IntToStr功能。不同的数字代表不同的错误。所以你可能要检查文档中不同的错误: [1].
文件程序
这些文件处理过程和函数位于系统单元。请参阅FPC文档了解更多信息: 系统单元参考.
- AssignFile(或者旧的Assign) - 将文件名赋给变量名
- Append - 以附加的方式打开已有的文件
- BlockRead - 读一个或多个记录到变量中
- BlockWrite - 从变量中写一个或多个记录
- CloseFile(或者旧的Close) - 关闭打开的文件
- EOF - 测试文件是否到文件尾
- Erase - 删除文件
- FilePos - 返回文件的当前指针位置
- FileSize - 返回当前文件的大小
- Flush - 将缓冲区的内容刷新到输出的文本文件中
- IOResult - 返回最新的I/O操作完成状态
- Read - Read from a text file into variable(从文本文件到变量)
- ReadLn - Read from a text file into variable and goto next line(读取文本文件并转到下一行)
- Reset - Opens a file for reading(打开文件并读取)
- Rewrite - Open file for writing(打开并写入文件)
- Seek - Change position in file(更改文件指针位置)
- SeekEOF - Set file position to end of file(设置文件位置为结尾)
- SeekEOLn - Set file position to end of line(文件位置设置为行结束)
- Truncate - Truncate the file at position(截断文件位置)
- Write - Write variable to a text file(写入变量到文本文件)
- WriteLn - Write variable to a text file and append newline(写入新行到文件)
示例
一个完整的处理文本文件的示例:
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文件为只读,再运行程序。这时程序会抛出异常,显示错误消息。
以下是如何追加/添加到文件的示例:
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.
读取文件:
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单元。
在下面的例子中,注意我们是如何封装处理文件在try... finally块中。这可以确保文件流对象总是释放(在finally... 部分),即使有文件访问(或其他)错误。
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 从性能角度来看。
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 字节(建议你使用集群文件系统或块大小的整数倍)并从缓冲区里读取数据。)
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中有一个)大文件等需要做些调整:
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到文件:
begin
with TStringList.Create do
try
Add('Hello');
SaveToFile('SomeFile.txt');
finally
Free;
end;
end;
为了写单个字符串到流可能需要使用以下过程:
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;