Perl学习笔记(5)-格式化输出与时间日期的处理

格式化输出

Perl 是一个非常强大的文本数据处理语言。Perl 中可以使用 format 来定义一个模板,然后使用 write 按指定模板输出数据。Perl 格式化定义语法格式如下

1
2
3
4
5
6
format FormatName =
fieldline
value_one, value_two, value_three
fieldline
value_one, value_two
.

参数解释:

1
2
3
4
FormatName :格式化名称。
fieldline :一个格式行,用来定义一个输出行的格式,类似 @,^,<,>,| 这样的字符。
value_one,value_two…… :数据行,用来向前面的格式行中插入值,都是perl的变量。
. :结束符号。

看一个简单的案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
biotest@ubuntu:~/perl/04time$ cat format01.pl
#!/usr/bin/perl
$text="google runoob taobao";
format STDOUT =
first:^<<<<< # left-justified 6 characters
$text
second: ^<<<<< # left-justified 6 characters
$text
third: ^<<<< # left-justified 5 characters, last "o" will be removed
$text
.
write
biotest@ubuntu:~/perl/04time$ perl format01.pl
first:google
second: runoob
third: taoba

格式行语法

  1. 格式行以@或者^开头,这些行不作任何形式的变量代换。
  2. @字段(不要同数组符号@相混淆)是普通的字段。
  3. @,^后的<, >,|长度决定了字段的长度,如果变量超出定义的长度,那么它将被截断。
  4. <, >,|还分别表示,左对齐,右对齐,居中对齐。
  5. ^字段用于多行文本块填充。

值域格式

格式 值域含义
@<<< 左对齐输出
@>>> 右对齐输出
`@ ` 中对齐输出
@##.## 固定精度数字
@* 多行文本

每个值域的第一个字符是行填充符,当使用@字符时,不做文本格式化。在上表中,除了多行值域@*,域宽都等于其指定的包含字符@在内的字符个数,例如@###.##表示7个字符的宽度,小数点前4个,小数点后2个,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
biotest@ubuntu:~/perl/04time$ cat format02.pl
#!/usr/bin/perl
format EMPLOYEE =
=============================
@<<<<<<<<<<<<<<<<<<<<<<@<<
$name,$age
@#####.##
$salary
=============================
.
select(STDOUT);
$~=EMPLOYEE;
@n=("Ali","Runoob","Tencent");
@a=(20,30,40);
@s=(2000.00,2500.00,4000.00);
$i=0;
foreach(@n){
$name=$_;
$age=$a[$i];
$salary=$s[$i++];
write;
}
biotest@ubuntu:~/perl/04time$ perl format02.pl
=============================
Ali 20
2000.00
=============================
=============================
Runoob 30
2500.00
=============================
=============================
Tencent 40
4000.00
=============================

格式变量

  1. $~ ($FORMAT_NAME) :格式名字;
  2. $^ ($FORMAT_TOP_NAME) :当前的表头格式名字存储;
  3. $% ($FORMAT_PAGE_NUMBER) :当前输出的页号;
  4. $= ($FORMAT_LINES_PER_PAGE) :每页中的行数;
  5. $| ($FORMAT_AUTOFLUSH) :是否自动刷新输出缓冲区存储;
  6. $^L ($FORMAT_FORMFEED) :在每一页(除了第一页)表头之前需要输出的字符串存储。

在下面的案例中,使用了$~进行格式化,案例如下所示:

1
2

如果不指定$~的情况下,会输出名为STDOUT的格式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
biotest@ubuntu:~/perl/04time$ cat format04.pl
#!/usr/bin/perl
write; # 没有指定$~,程序会会寻找名为STDOUT的格式
format STDOUT=
----------------
STDOUT formats
----------------
.
biotest@ubuntu:~/perl/04time$ perl format04.pl
----------------
STDOUT formats
----------------

在下面的案例中,添加报表头部信息来说明$^$FORMAT_TOP_NAME变量的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
biotest@ubuntu:~/perl/04time$ cat format05.pl
#!/usr/bin/perl
format EMPLOYEE =
====================================
@<<<<<<<<<<<<<<<<<<<<<<< @<<
$name, $age
@#####.##
$salary
====================================
.
format EMPLOYEE_TOP=
====================================
Name Age Page @<
$%
====================================
.
select(STDOUT);
$~ = EMPLOYEE;
$^ = EMPLOYEE_TOP;
@n=("Ali","Runoob","Tencent");
@a=(20,30,40);
@s=(2000.00,2500.00,4000.000);
$i=0;
foreach(@n){
$name=$_;
$age=$a[$i];
$salary=$s[$i++];
write;
}
biotest@ubuntu:~/perl/04time$ perl format05.pl
====================================
Name Age Page 1
====================================
====================================
Ali 20
2000.00
====================================
====================================
Runoob 30
2500.00
====================================
====================================
Tencent 40
4000.00
====================================

输出到其它文件

默认情况下函数write将结果输出到标准输出文件STDOUT,我们也可以使它将结果输出到任意其它的文件中。最简单的方法就是把文件变量作为参数传递给write,例如write(MYFILE),此时write就用缺省的名为MYFILE的打印格式输出到文件MYFILE中。但是这样就不能用$~变量来改变所使用的打印格式。系统变量$~只对默认文件变量起作用,我们可以改变默认文件变量,改变$~,再调用write,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
biotest@ubuntu:~/perl/04time$ cat myfile.pl
#!/usr/bin/perl
if(open(MYFILE,">tmp")){
$~="MYFORMAT";
write MYFILE;
format MYFILE=
========================
Input into file
========================
.
close MYFILE;
}
biotest@ubuntu:~/perl/04time$ perl myfile.pl
biotest@ubuntu:~/perl/04time$ cat tmp
========================
Input into file
========================

以使用select改变默认文件变量时,它返回当前默认文件变量的内部表示,这样我们就可以创建子程序,按自己的想法输出,又不影响程序的其它部分,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
biotest@ubuntu:~/perl/04time$ cat select.pl
#!/usr/bin/perl
if(open(MYFILE,">>tmp")){
select(MYFILE);
$~="OTHER";
write;
format OTHER=
========================
input something into file
========================
.
close MYFILE;
}
biotest@ubuntu:~/perl/04time$ perl select.pl
biotest@ubuntu:~/perl/04time$ cat tmp
========================
Input into file
========================
========================
input something into file
========================

Perl中处理时间的函数

Perl中处理时间的函数有以下这些:

  1. time() 函数:返回从1970年1月1日起累计的秒数
  2. localtime() 函数:获取本地时区时间
  3. gmtime() 函数: 获取格林威治时间

当前时间和日期

localtime()函数返回当前的时间和日期。
以下 9 个符号代表不同的时间日期参数:

1
2
3
4
5
6
7
8
9
sec, # 秒, 0 到 61
min, # 分钟, 0 到 59
hour, # 小时, 0 到 24
mday, # 天, 1 到 31
mon, # 月, 0 到 11
year, # 年,从 1900 开始
wday, # 星期几,0-6,0表示周日
yday, # 一年中的第几天,0-364,365
isdst # 如果夏令时有效,则为真

使用案例如下所示:

1
2
3
4
5
6
7
8
9
biotest@ubuntu:~/perl/04time$ cat time.pl
@months = qw(Janurary February March April May June July August September October November December);
@days = qw(Sunday Saturday Monday Tuesday Wednesday Friday);
($sec, $min, $hour, $mday,$mon,$year, $wday, $yday, $isdst) = localtime() ;
print"$mday $months[$mon] $days[$wday]\n";
biotest@ubuntu:~/perl/04time$ perl time.pl
18 May Friday

如果直接调用 localtime() ,它返回系统当期设置时区的时间,如下所示:

1
2
3
4
5
6
7
biotest@ubuntu:~/perl/04time$ cat localtime.pl
#!/usr/bin/perl
$datenow=localtime();
print "Now time is $datenow\n";
biotest@ubuntu:~/perl/04time$ perl localtime.pl
Now time is Fri May 18 16:05:51 2018

格林威治时间 (GMT)

函数 gmtime() 与 localtime() 类似,但它返回标准格林威治时间,如下所示:

1
2
3
4
5
6
7
8
9
10
11
biotest@ubuntu:~/perl/04time$ cat gmt.pl
#!/usr/bin/perl
$Chongqing_time = localtime();
$gmt_time = gmtime();
print "Chongqing time is $Chongqing_time\n";
print "GMT time is $gmt_time\n";
biotest@ubuntu:~/perl/04time$ perl gmt.pl
Chongqing time is Fri May 18 16:07:05 2018
GMT time is Fri May 18 23:07:05 2018

运行结果可以看出,中国当地时间和格林威治时间相差了8小时,中国是东八区。

格式化日期和时间

使用localtime()函数的 9 个时间元素来可以输出需要制定的格式时间。格式化输出使用 printf() 函数,如下所示:

1
2
3
4
5
6
7
8
9
10
biotest@ubuntu:~/perl/04time$ cat format_time.pl
#!/usr/bin/perl
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime();
printf("Formated time is HH:MM:SS\n");
printf("%02d:%02d:%02d\n", $hour, $min, $sec);
biotest@ubuntu:~/perl/04time$ perl format_time.pl
Formated time is HH:MM:SS
16:10:03

新纪元时间(Epoch Time)

使用 time() 函数来获取新纪元时间,该函数返回从1970年1月1日起累计的秒数,如下所示:

1
2
3
4
5
6
7
biotest@ubuntu:~/perl/04time$ cat epoch_time.pl
#!/usr/bin/perl
$epoch_time = time();
print "Accumulated seconds from 1970.1.1. is $epoch_time\n";
biotest@ubuntu:~/perl/04time$ perl epoch_time.pl
Accumulated seconds from 1970.1.1. is 1526685082

也可以输出一个用户想要的时间格式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
biotest@ubuntu:~/perl/04time$ cat custom_time.pl
#!/usr/bin/perl
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
print "Current time and date";
printf ("%d-%d-%d %d:%d:%d", $year+1900, $mon +1, $mday, $hour, $min, $sec);
print "\n";
$epoch_time = time();
$epoch_time = $epoch_time - 24*60*60;
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($epoch_time);
print "Yesterady time and date is: ";
printf ("%d-%d-%d %d:%d:%d", $year+1900, $mon +1, $mday, $hour, $min, $sec);
print "\n";
biotest@ubuntu:~/perl/04time$ perl custom_time.pl
Current time and date2018-5-18 16:12:46
Yesterady time and date is: 2018-5-17 16:12:46

POSIX 函数 strftime()

函数strftime()可以将时间格式化为我们想要的格式。
下表列出了一些格式化的符号,* 号表示想要依赖本地时间:

符号 描述 实例
%a 星期几的简称( Sun..Sat) * Thu
%A 星期几的全称( Sunday..Saturday) * Thursday
%b 月的简称(Jan..Dec) * Aug
%B 月的全称(January..December) * August
%c 日期和时间 * Thu Aug 23 14:55:02 2001
%C 年份除于100,并取整 (00-99) 20
%d 一个月的第几天 (01-31) 23
%D 日期, MM/DD/YY 相等于%m/%d/%y 08/23/01
%e 一个月的第几天,使用空格填充个位数 ( 1-31) 23
%F YYYY-MM-DD 的简写类似于 %Y-%m-%d 2001/8/23
%g 年份的最后两位数 (00-99) 1
%g 2001
%h 月的简称 *(和%b选项相同) Aug
%H 24 小时制 (00-23) 14
%I 12 小时制 (01-12) 02
%j 一年的第几天 (001-366) 235
%m 月 (01-12) 08
%M 分钟 (00-59) 55
%n 新行 (‘\n’)
%p 显示出AM或PM PM
%r 时间(hh:mm:ss AM或PM),12小时 * 02:55:02 pm
%R 24 小时 HH:MM 时间格式,相等于 %H:%M 14:55
%S 秒数 (00-61) 02
%t 水平制表符 (‘\t’)
%T 时间(24小时制)(hh:mm:ss),相等于%H:%M:%S 14:55
%u ISO 8601 的星期几格式,星期一为1 (1-7) 4
%U 一年中的第几周,星期天为第一天(00-53) 33
%V ISO 8601 第几周 (00-53) 34
%w 一个星期的第几天(0代表星期天) (0-6) 4
%W 一年的第几个星期,星期一为第一天 (00-53) 34
%x 显示日期的格式(mm/dd/yy) * 08/23/01
%X 显示时间格式 * 14:55:02
%y 年,两位数 (00-99) 01
%Y 2018
%z ISO 8601与UTC的时区偏移(1 minute=1, 1 hour=100) +100
%Z 当前时区的名称,如”中国标准时间” * CDT
%% % 符号 %

使用案例如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
biotest@ubuntu:~/perl/04time$ cat strftime.pl
#!/usr/bin/perl
use POSIX qw(strftime);
$current_time = strftime "%Y-%m-%d %H:%M:%S", localtime;
printf ("Time and Date - $current_time\n");
$current_time = strftime "%Y-%m-%d %H:%M:%S", gmtime;
printf "Time and Date - $current_time\n";
biotest@ubuntu:~/perl/04time$ perl strftime.pl
Time and Date - 2018-05-18 16:20:05
Time and Date - 2018-05-18 23:20:05

参考资料

Perl教程|菜鸟教程