Skip to content

Perl 格式化

重要提示: Perl 中用于报告生成的 format 和 write 功能在很大程度上被视为传统 (legacy) 功能。虽然它们是 Perl 核心的一部分,但现代 Perl 开发绝大多数倾向于使用模板引擎 (templating engines)(例如 Template Toolkit、Text::Template、Mojo::Template)来生成文本报告、HTML 或其他结构化文本输出。这些引擎提供了更大的灵活性、更强大的功能和更好的可维护性。本节介绍 Perl 的格式化功能是为了完整性,但在新项目中通常不鼓励使用它们。

Perl 的 format 功能允许您定义模板来生成简单的固定列报告。您定义一个格式,然后使用 write 关键字根据该模板输出数据。

使用 format 关键字声明一个格式,后跟一个名称(通常与文件句柄名称绑定),一个等号,然后是定义布局的行,最后以单独一行上的句点 (.) 结束。

语法:

format FormatName =
field_line_1
value_line_1_var1, value_line_1_var2, ...
field_line_2
value_line_2_var1, value_line_2_var2, ...
.
  • FormatName:格式的名称。默认情况下,Perl 会查找与当前输出文件句柄同名的格式(例如,如果打印到标准输出,则查找 STDOUT)。
  • field_line:包含字面文本和图片字段 (picture fields)(占位符 fieldholders),它们定义了如何格式化值。
  • value_line:变量或表达式的列表,其值将被插入到前面 field_line 中的占位符中。

占位符指定了数据在 field_line 中的位置和格式化方式。

  • @<<<< : 左对齐文本字段。< 的数量加上 @ 决定了字段宽度。
  • @>>>> : 右对齐文本字段。
  • @|||| : 居中文本字段。
  • @###.## : 数值字段。# 代表数字位置。. 是小数点。输出是右对齐的。
  • @* : 多行文本字段。用于可能包含换行符的值。值会被打印出来,如果它没有填满当前行,format 处理会在下一行继续,打印剩余的值。

格式定义的示例:

#!/usr/bin/env perl
use strict;
use warnings;
# 将被格式使用的变量
our ($name, $age, $salary);
format EMPLOYEE_RECORD =
=====================================
Name: @<<<<<<<<<<<<<<<<<<<<< Age: @<<
$name, $age
Salary: @#####.##
$salary
-------------------------------------
.

在此示例中,$name 将在 25 个字符宽的字段(包括 @)中左对齐,$age 在 2 个字符宽的字段中,$salary 格式化为小数点前最多 5 位数字、小数点后 2 位数字的数值。

write 关键字使用定义的格式输出数据。write 将输出发送到当前选定的文件句柄(参见 select())。要使用特定的格式,您可以使用特殊变量 $~(如果激活了 use English;,则使用 $FORMAT_NAME)将其与文件句柄关联。

#!/usr/bin/env perl
use strict;
use warnings;
# 声明将被格式使用的包变量
our ($name, $age, $salary);
# 定义格式(同上)
format EMPLOYEE_RECORD =
=====================================
Name: @<<<<<<<<<<<<<<<<<<<<< Age: @<<
$name, $age
Salary: @#####.##
$salary
-------------------------------------
.
# 选择 STDOUT 作为 write 的输出文件句柄
select(STDOUT);
# 将 EMPLOYEE_RECORD 格式与 STDOUT 关联
$~ = "EMPLOYEE_RECORD";
# 报告数据
my @employees = (
{ name => "Alice Wonderland", age => 30, salary => 55000.75 },
{ name => "Bob The Builder", age => 42, salary => 62000.50 },
{ name => "Charlie Brown", age => 8, salary => 10.00 },
);
foreach my $emp_data (@employees) {
# 为格式使用的包变量赋值
$name = $emp_data->{name};
$age = $emp_data->{age};
$salary = $emp_data->{salary};
write; # 使用 EMPLOYEE_RECORD 格式将一条记录输出到 STDOUT
}

执行上述代码将产生:

=====================================
Name: Alice Wonderland Age: 30
Salary: 55000.75
-------------------------------------
=====================================
Name: Bob The Builder Age: 42
Salary: 62000.50
-------------------------------------
=====================================
Name: Charlie Brown Age: 8
Salary: 10.00
-------------------------------------

您可以定义一个页眉格式,它将在每页的顶部打印。此页眉格式的名称通常是 FILEHANDLE_TOP(例如 STDOUT_TOP)。您可以将页眉格式的名称赋给特殊变量 $^(如果激活了 use English;,则使用 $FORMAT_TOP_NAME)。

#!/usr/bin/env perl
use strict;
use warnings;
our ($name, $age, $salary); # 用于 EMPLOYEE_RECORD
our ($page_num); # 用于 STDOUT_TOP
format STDOUT_TOP =
Employee Report Page: @<
$page_num
----------------------------------------------------------
Name Age Salary
----------------------------------------------------------
.
format EMPLOYEE_RECORD =
@<<<<<<<<<<<<<<<<<<<<<<< @<< @##########.##
$name, $age, $salary
.
select(STDOUT);
$~ = "EMPLOYEE_RECORD";
$^ = "STDOUT_TOP"; # 设置页眉格式
# 模拟多名员工,以可能跨页显示(如果页长设置得较小)
my @employees_data = (
{ name => "Alice", age => 30, salary => 55000 },
{ name => "Bob", age => 42, salary => 62000 },
# ... 添加更多员工以查看分页(如果 $= 设置得较小)
);
$page_num = 0; # 初始化页码,将由 $% 或手动更新
foreach my $emp_data (@employees_data) {
# 在每次 write 之前,Perl 会检查是否需要新页。
# 如果需要,它会递增 $FORMAT_PAGE_NUMBER ($%) 并打印 _TOP 格式。
# 我们需要确保 $page_num 反映这一点,以便在页眉中使用。
$page_num = $%; # $FORMAT_PAGE_NUMBER
if ($FORMAT_LINES_LEFT <= 3) { # 检查是否接近底部,页面分隔的启发式判断
$page_num++; # 如果需要,手动预判,或依赖 write 之前的 $%
}
$name = $emp_data->{name};
$age = $emp_data->{age};
$salary = $emp_data->{salary};
write;
}

使用格式化功能时,Perl 在一定程度上会自动处理分页。

  • $%(或 $FORMAT_PAGE_NUMBER):包含当前页码。当发生分页时会自动递增。
  • $=(或 $FORMAT_LINES_PER_PAGE):指定每页可打印的行数。默认值为 60。您可以设置此变量来控制页长。
  • $-(或 $FORMAT_LINES_LEFT):包含当前页面剩余的行数。当此值达到 0(或由于多行字段而小于 0)时,会开始新页,打印 _TOP 格式,并将 $- 重置为 $=。

要将页码包含在您的页眉中(如上面的 STDOUT_TOP 示例),请在页眉格式的值行中使用 $%。

format STDOUT_TOP =
Employee Report Page @<<<
$%
------------------------------------
Name Age Salary
------------------------------------
.

Perl 没有与页眉的 $^ 相对应的页脚自动机制。如果您需要页脚,必须手动实现。这通常需要在每次 write 之前检查 $-(剩余行数)。如果 $- 太低,以至于下一条记录(加上页脚)无法容纳,您可以使用单独的 format 定义打印页脚,然后手动触发分页(例如,通过打印换行符直到 $- 为 0,或通过写入一个特殊的填充页面的空白记录),或者打印页脚后再打印新页的页眉。

format EMPLOYEE_FOOTER =
End of Page @<.
$%
====================================
.

然后您将有条件地调用 write EMPLOYEE_FOOTER;。

如前所述,对于任何需要报告生成或文本模板的新开发,请考虑使用现代的 CPAN 模块,如 Template Toolkit、Text::Template 或其他提供远超 Perl 内置格式系统功能的模块,并且更易于维护。