第四章 Perl6 文件操作
译注:
在原文标题后加上小括号以表示其为方法名,以与一般标题加以区别。exit()
在学习怎么处理文件之前,我们先来了解一些实用的函数。
exit()
能够使程序在任何调用它的地方退出,即无需运行到最后一行就结束。
tutorial/files/exit.p6
#!/usr/bin/env perl6
use v6;
say "hello";
exit;
say "world";
warn()
warn()
能够将一条错误信息打印至标准错误流(STDERR),且程序将保持运行,不会因此而退出。
tutorial/files/warn.p6
#!/usr/bin/env perl6
use v6;
warn "This is a warning";
say "Hello World";
译注:
在控制台时将打印出This is a warning in block <unit> at <unknown file> line 1
die()
die()
将打印一条信息到标准错误流并终止程序的运行。
tutorial/files/die.p6
#!/usr/bin/env perl6
use v6;
say "Before calling die";
die "This will kill the script";
say "This will not show up";
Twigils与特殊变量
译注:
twigil一词只出现在Perl语言的特殊语境下,是一种能够表示变量名作用域的符号。
关于twigil的更多细节请参见Twigils。
Perl6中存在大量的特殊变量,为了将其与常规的变量区分开来,我们为这些特殊变量加上一种称作twigil的二级前缀。
通常情况下,用户声明的变量前面会加有一种叫做sigil的前缀,如$
、@
以及%
等。
系统变量在sigil与变量名之间也有一个特殊的字符(*)。
例如:
$*PROGRAM-NAME
:当前运行脚本的相对路径$*PROGRAM
:当前运行脚本的文件名称$*CWD
:当前工作路径$*IN
:标准输入流(STDIN),你可以使用$*IN.get
来读取其中的一行$*PID
:当前进程PID$*EXECUTABLE-NAME
:运行当前脚本的二进制文件名称$*EXECUTABLE
:运行当前脚本的二进制文件的绝对路径$*TMPDIR
:用于存放临时文件的绝对路径
你可以在S28中了解到更多相关信息。
读取文件
与使用其他高级语言时一样,人们需要打开一个文件并对其进行读写。Perl6中可以使用IO
中的方法open()
来打开一个文件。
open()
能够接收多个参数,其中文件名与读写模式两者比较重要。如果想读取文件那么模式需要为:r
。
文件打开成功后会返回一个存放在标量中的文件句柄(handler),如果失败则抛出异常。
译注:
原文中作者说失败会返回undef
,此说法已经过时。
$fh = open $filename, :r
一旦我们拥有了打开后的文件句柄,就可以使用方法$fh.get
来读取给定文件里的一行。
我们可以反复使用get()
来从文件中读取更多内容,然而有其他方法能够更好地做到这一点。
在S32-setting-library/IO.pod中能够找到Perl6的所有IO规范。
tutorial/files/read_one_line.p6
#!/usr/bin/env perl6
use v6;
my $filename = $*PROGRAM-NAME;
my $fh = open $filename;
my $line = $fh.get;
say $line;
使用get()读取所有行
tutorial/files/read_file_line_by_line.p6
#!/usr/bin/env perl6
use v6;
my $filename = $*PROGRAM-NAME;
my $fh = open $filename;
while (defined my $line = $fh.get) {
say $line;
}
逐行读取文件
tutorial/files/read_file.p6
#!/usr/bin/env perl6
use v6;
my $filename = $*PROGRAM-NAME;
my $fh = open $filename;
for $fh.lines -> $line {
say $line;
}
lines()
方法能够返回文件中的前几行(由参数控制)或者所有行(默认)。在上述代码中,其返回一个懒加载的列表,然后我们再使用for
循环将其中的内容逐条输出。
此脚本与unix系统中cat
命令的功能相近。
写文件
若希望对文件进行写操作,我们首先应该通过写模式:w
打开文件。同样,打开成功后我们将获得一个文件句柄。
可以对文件句柄(IO句柄)调用一些常规方法如print()
、say()
或者printf()
。
tutorial/files/write_file.p6
#!/usr/bin/env perl6
use v6;
my $filename = "temp.txt";
my $fh = open $filename, :w;
$fh.say("hello world"); # 成功则返回Ture
$fh.close; # 成功则返回Ture
文件模式
:r | 只读模式 |
:w | 只写模式 |
:rw | 读写模式 |
:a | 追加模式 |
译注:
原文中作者遗漏了读写模式。
更多关于文件/编码/新行模式的信息请参见IO。
slurp()与spurt()
Perl6中内置了一种读取文件的模式,用来“啜食”(slurp)文件中的内容,即读取文件中的全部信息到一个标量中。
tutorial/files/slurp_file.p6
#!/usr/bin/env perl6
use v6;
my $filename = $*PROGRAM-NAME;
my $data = slurp $filename;
say $data.bytes;
译注:
Perl6中同样有方法将内容“喷射”(spurt)进文件中。my $newdata = "New scores: Paul 10 Paulie 9 Paulo 11"; spurt "newdatafile.txt", $newdata;
将文件逐行读取至数组
我们还没有学习有关数组的知识,在此之前我可以先告诉你,如果将slurp()
调用的返回值放进一个数组,那么整个文件内容将变成数组中第一个也是唯一的一个元素。
如果我们希望将文件中的每一行都作为一个元素依次放进数组中呢?
lines()
函数可以做到这一点。
tutorial/files/read_file_into_array.p6
#!/usr/bin/env perl6
use v6;
my $filename = $*PROGRAM-NAME;
# 将文件中全部内容读取至数组的第一个元素
my @content = slurp $filename;
say @content.elems;
# 读取文件中全部内容,每一行作为数组中的一个元素
my @rows = lines $filename.IO;
say @rows.elems;
练习:利用文件IO计算其中数字的和
创建一个文件,其中的每一行有一个数字。
打印这些数字的和。
tutorial/numbers.txt
3
8
19
-7
13
结果为36。
修改程序,打印出平均值、最小值和最大值。
同时思考如何计算中值与标准差。
答案:利用文件IO计算其中数字的和
tutorial/files/statistics.p6
#!/usr/bin/env perl6
use v6;
my $filename = 'examples/numbers.txt';
my $total;
my $count;
my $min;
my $max;
if (my $fh = open $filename, :r) {
for $fh.lines -> $line {
if (not $count) {
$min = $max = $line;
}
$total += $line;
if ($min > $line) {
$min = $line;
}
if ($max < $line) {
$max = $line;
}
$count++;
}
my $average = $total / $count;
say "Total: $total, Count: $count Average: $average Min: $min Max: $max";
} else {
say "Could not open '$filename'";
}
# 思考:答案有一个小瑕疵——文件中没有任何值时该怎么办?