Sign in Join
Wiki copytemporary

File Handling In Pascal/zh CN: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: File Handling In Pascal/zh CN

OldNewLine
1+大多数程序员都需要知道如何操作文件。文件可以用来保存用户设置、错误日志等等。在这里我将教你如何操作文本文件。
2+
3+# 旧程序风格
4+
5+使用标准Pascal文件(非面向对象),你可以使用一个文本文件类型,它允许你将字符串写入到文件或创建自己的文件类型。
6+
7+``` pascal
8+...
9+type
10+ TIntegerFile = file of Integer; // 允许你将整数写入文件
11+ TPCharFile = file of PChar; // PChars写入到文件
12+ TStringFile = file of String; // 字符串写入到文件
13+...
14+```
15+
16+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.
17+
18+(~~如果我们只做了TStringFile = File,那么它不能被写入任何东西!此外,你不能将整数写入到TStringFile。因为它是一个字符串文件。更好的使用文件类型来存储不同类型的值。~~)
19+
20+## IO
21+
22+IO is the file handling thingy for Pascal.(~~IO是Pascal文件处理的~~),它告诉编译器如何处理IO错误:抛出一个异常或将结果存储到IOResult变量。 因为它是一个编译器指令,所以:
23+
24+``` pascal
25+{$I-} // 关闭检查。将所有的错误存入IOResult变量
26+{$I+} // 把它重新打开,将导致EInOutError异常
27+```
28+
29+通过禁用/关闭 \$I, 文件操作结果进入IOResult变量。这是一个[[Cardinal|基数数字类型]]. 所以,如果你想写IOResult,你必须使用IntToStr功能。不同的数字代表不同的错误。所以你可能要检查文档中不同的错误: [1](http://www.freepascal.org/docs-html/rtl/system/ioresult.html).
30+
31+## 文件程序
32+
33+这些文件处理过程和函数位于系统单元。请参阅FPC文档了解更多信息: [系统单元参考](http://www.freepascal.org/docs-html/rtl/system/index-5.html).
34+
35+- **AssignFile**(或者旧的**Assign**) - 将文件名赋给变量名
36+- **Append** - 以附加的方式打开已有的文件
37+- **BlockRead** - 读一个或多个记录到变量中
38+- **BlockWrite** - 从变量中写一个或多个记录
39+- **CloseFile**(或者旧的**Close**) - 关闭打开的文件
40+- **EOF** - 测试文件是否到文件尾
41+- **Erase** - 删除文件
42+- **FilePos** - 返回文件的当前指针位置
43+- **FileSize** - 返回当前文件的大小
44+- **Flush** - 将缓冲区的内容刷新到输出的文本文件中
45+- **IOResult** - 返回最新的I/O操作完成状态
46+- **Read** - Read from a text file into variable(从文本文件到变量)
47+- **ReadLn** - Read from a text file into variable and goto next line(读取文本文件并转到下一行)
48+- **Reset** - Opens a file for reading(打开文件并读取)
49+- **Rewrite** - Open file for writing(打开并写入文件)
50+- **Seek** - Change position in file(更改文件指针位置)
51+- **SeekEOF** - Set file position to end of file(设置文件位置为结尾)
52+- **SeekEOLn** - Set file position to end of line(文件位置设置为行结束)
53+- **Truncate** - Truncate the file at position(截断文件位置)
54+- **Write** - Write variable to a text file(写入变量到文本文件)
55+- **WriteLn** - Write variable to a text file and append newline(写入新行到文件)
56+
57+## 示例
58+
59+一个完整的处理文本文件的示例:
60+
61+``` pascal
62+program FileTest;
63+
64+{$mode objfpc} // 不要忘了这个
65+
66+uses
67+ Sysutils;
68+
69+var
70+ FileVar: TextFile;
71+
72+begin
73+ WriteLn('File Test');
74+ AssignFile(FileVar, 'Test.txt'); // 你不需要输出 .txt 但现在需要
75+ {$I+} // 使用异常处理
76+ try
77+ Rewrite(FileVar); // 创建文件
78+ Writeln(FileVar,'Hello');
79+ // Use CloseFile rather than Close as Close is used in other units as well
80+ CloseFile(FileVar);
81+ except
82+ on E: EInOutError do
83+ begin
84+ Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
85+ end;
86+ end;
87+ WriteLn('Program finished. Press enter to stop.');
88+ ReadLn;
89+end.
90+```
91+
92+任意编辑器打开这个文件,你将看到'''Hello'! 你可以设置Test.txt文件为只读,再运行程序。这时程序会抛出异常,显示错误消息。
93+
94+> **Note:** 我们使用异常处理({\$I+}),它是对多个文件操作及处理错误时的一个简单方法。你也可以使用{\$I-},但你必须在每次操作后检查但你必须检查IOResult,并修改你的下一步操作。
95+
96+以下是如何追加/添加到文件的示例:
97+
98+``` pascal
99+program EditFile;
100+
101+{$mode objfpc}
102+
103+uses
104+ Sysutils;
105+
106+var
107+ File1: TextFile;
108+
109+begin
110+ WriteLn('Append file');
111+ {$I+}
112+ try
113+ AssignFile(File1, 'File.txt');
114+ {
115+ 我这里测试时,原版示例不能执行报错 Error: Wrong number of parameters specified for call to "Append"
116+ 在Lazarus 1.2.2,FPC2.6.4上
117+ }
118+ // Append(File1, 'adding some text...');
119+ { 修改后的程序 }
120+ Append(File1);
121+ Writeln(FileVar,'adding some text...');
122+ Writeln
123+ CloseFile(File1);
124+ except
125+ on E: EInOutError do
126+ begin
127+ Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
128+ end;
129+ end;
130+ WriteLn('Program finished. Press enter to stop.');
131+ Readln;
132+end.
133+```
134+
135+读取文件:
136+
137+``` pascal
138+program ReadFile;
139+
140+{$mode objfpc}
141+
142+uses
143+ Sysutils;
144+
145+var
146+ File1: TextFile;
147+ Str: String;
148+
149+begin
150+ Writeln('File Reading:');
151+ AssignFile(File1, 'File.txt');
152+ {$I+}
153+ try
154+ Reset(File1);
155+ repeat
156+ Readln(File1, Str); // 从文件中读取一行
157+ Writeln(Str); // 显示这行
158+ until(EOF(File1)); // EOF(文件结束)程序将继续读取直到文件结尾。
159+ CloseFile(File1);
160+ except
161+ on E: EInOutError do
162+ begin
163+ Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
164+ end;
165+ end;
166+ WriteLn('Program finished. Press enter to stop.');
167+ Readln;
168+end.
169+```
170+
171+可以做一些文件处理使用字符而不是字符串。这使得它看起来酷:D。
172+
173+# 对象风格
174+
175+除了上面提到的旧式文件处理例程,在新系统更高的抽象层次里使用流(数据流)的概念,这意味着作为一个程序员在处理文件时执行更少的步骤。
176+
177+此外,大部分字符串处理类可以加载(保存)内容从(到)文件。这些方法通常是SaveToFile和LoadFromFile。很多其他对象(像Lazarus网格)也有类似的功能,包括Lazarus数据集(DBExport);it pays to look through the documentation/source code before trying to roll your own import/export routines.
178+
179+## 二进制文件
180+
181+为操作文件应该使用直接使用TFileStream。这个类封装了系统程序,FileCreate,FileRead,FileWrite,FileSeek和FileClose它在FileUtil单元。
182+
183+[IO 例程](http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html)
184+
185+在下面的例子中,注意我们是如何封装处理文件在try... finally块中。这可以确保文件流对象总是释放(在finally... 部分),即使有文件访问(或其他)错误。
186+
187+``` pascal
188+var
189+ Buffer: array[0..9999] of Byte;
190+begin
191+ with TFileStream.Create('SomeFile.bin', fmCreate) do
192+ try
193+ Seek('Hello');
194+ Write(Buffer, SizeOf(Buffer));
195+ finally
196+ Free;
197+ end;
198+end;
199+```
200+
201+你可以加载整个文件到内存中,如果文件大小大于系统可用内存,你的操作系统会开始使用页面/交换文件,making the exercise useless 从性能角度来看。
202+
203+``` pascal
204+begin
205+ with TMemoryStream.Create do
206+ try
207+ LoadFromFile('SomeFile.bin');
208+ Seek(0, soEnd);
209+ Write(Ord('A'), 1);
210+ SaveToFile('SomeFile.bin');
211+ finally
212+ Free;
213+ end;
214+end;
215+```
216+
217+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.
218+
219+(~~你可能需要读取许多Gb大文件,比方说4096 字节(建议你使用集群文件系统或块大小的整数倍)并从缓冲区里读取数据。~~)
220+
221+``` pascal
222+var
223+ TotalBytesRead, BytesRead : Int64;
224+ Buffer : array [0..4095] of byte; // 或 array [0..4095] 为 char
225+ FileStream : TFileStream;
226+
227+try
228+ FileStream := TFileStream.Create;
229+ FileStream.Position := 0; // 确保在文件开始位置
230+ while TotalBytesRead <= FileStream.Size do // 当总读取大小 <= 文件大小
231+ begin
232+ BytesRead := FileStream.Read(Buffer,sizeof(Buffer)); // 读取 4096 字节数据
233+ inc(TotalBytesRead, BytesRead); // 增加 TotalByteRead 缓冲区的大小,也就是说 4096 字节
234+ // 做一些与缓冲数据相关的操作
235+ end;
236+```
237+
238+### 复制文件
239+
240+与上述,我们可以实现一个简单的FileCopy功能(FreePascal has none in its RTL although Lazarus has one) - adjust as needed for bigger files etc:~~(FreePascal现在没有Lzarus中的RTL中有一个)大文件等需要做些调整:~~
241+
242+``` pascal
243+function FileCopy(Source, Target: string): boolean;
244+// 复制文件 source 到 target;覆盖目标文件
245+// 在内存中缓存整个文件
246+// 成功返回 true,失败返回false
247+var
248+ MemBuffer: TMemoryStream;
249+begin
250+ result:=false;
251+ MemBuffer:=TMemoryStream.Create;
252+ try
253+ try
254+ MemBuffer.LoadFromFile(Source);
255+ MemBuffer.Position:=0;
256+ MemBuffer.SaveToFile(Target); //可能是源文件相同
257+ result:=true;
258+ except
259+ result:=false; //忽略异常,转换为错误代码
260+ end;
261+ finally
262+ MemBuffer.Free;
263+ end;
264+end;
265+```
266+
267+## 文本文件
268+
269+一般情况下,对于文本文件可以使用 TStringList 类将整个文件加载到内存中,并可以对行进行简单存取。当然,你也可以保存StringList到文件:
270+
271+``` pascal
272+begin
273+ with TStringList.Create do
274+ try
275+ Add('Hello');
276+ SaveToFile('SomeFile.txt');
277+ finally
278+ Free;
279+ end;
280+end;
281+```
282+
283+为了写单个字符串到流可能需要使用以下过程:
284+
285+``` pascal
286+procedure SaveStringToPath(theString, filePath: String);
287+var
288+ textFile: TFileStream = nil;
289+ textLength: integer;
290+ stringBuffer: ^String;
291+begin
292+ textLength := length(theString);
293+ try
294+ textFile := TFileStream.Create(filePath, fmOpenWrite or fmCreate);
295+ { write string to stream while avoiding to write the initial length }
296+ { 写字符串到流,同时避免写入超过最初长度 }
297+ stringBuffer := @theString + 1;
298+ textFile.WriteBuffer(stringBuffer^, textLength);
299+ finally
300+ if textFile <> nil then textFile.Free;
301+ end;
302+end;
303+```
304+
305+## 参见
306+
307+- [[File]]
308+
309+---
310+*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](/wiki-contact).*

Back to history