1 命名規(guī)則
1.1 文件命名
文件名稱統(tǒng)一用英文字母(大小寫)、數(shù)字和下劃線的組合,長(zhǎng)度一般不超過20個(gè)字符,文件命名體現(xiàn)功能的含義,正式發(fā)布版本不能加入作者信息。Perl Scripts 文件的擴(kuò)展名必須是".pl",Perl Module文件的擴(kuò)展名必須是".pm"。
正確:
lucent_file_parser.pl
不是:
lucent_file.p
1.2 標(biāo)識(shí)符
采用語法模板來定義標(biāo)識(shí)符的名字,命名必須有一定的實(shí)際意義,由英文字母組成,中間可以根據(jù)語義的連續(xù)性,使用下劃線連接。
1.2.1 變量
局部變量必須由小寫字母和下劃線組成,常量名必須由大寫字母和下劃線組成。由多個(gè)單詞組成的名字里,使用下劃線"_"把多個(gè)單詞分開。全局變量以’g_’開頭,其余部分的命名規(guī)則和局部變量相同。每個(gè)全局變量必須有注釋說明其作用。
正確:
my $next_node;
不是:
my $node;
1.2.2 包和類
包、類的命名采用大小寫混合、首字母大寫的方法。
正確:
IO::Controller
1.2.3 標(biāo)識(shí)符縮寫
標(biāo)識(shí)符縮寫時(shí)要保留單詞的開始字母,不是輔音字母的簡(jiǎn)寫。
正確:
use List::Util qw( max );
DESC:
for my $desc (@orig_strs) {
my $len = length($desc);
next DESC if ($len > $UPPER_LIM);
$max_len = max($max_len, $len);
}
不是:
use List::Util qw( max );
DSCN:
for my $dscn (@rgnl_strgs) {
my $lngh = length $dscn;
next DSCN if $lngh > $UPPR_LMT;
$mx_lngh = max($mx_lngh, $lngh);
}
1.2.4 函數(shù)
由小寫字母、下劃線組成。
正確:
sub max
{
……
}
sub get_msc_name
{
……
}
不是:
sub getMscName
{
……
}
1.3 布爾類型
boolean類型的變量,或返回boolean類型的值的函數(shù),在命名時(shí)要反映其屬性,必須用is或者h(yuǎn)as開頭。
正確:
sub is_valid;
sub is_metadata_available_for;
sub has_end_tag;
my $has_loading_finished;
my $has_found_bad_record;
# and later...
if (is_valid($next_record) && !$has_loading_finished) {
METADATA:
while (is_metadata_available_for($next_record)) {
push @metadata, get_metadata_for($next_record);
last METADATA if (has_end_tag($next_record));
}
}
else {
$has_found_bad_record = 1;
}
1.4 數(shù)組和哈希
數(shù)組類型的變量采用復(fù)數(shù),hash類型的變量采用單數(shù)。要用undef顯式釋放變量空間。
正確:
my %option;
my %title_of;
my %count_for;
my %is_available;
# and later...
if ($option{'count_all'} && $title_of{$next_book} =~ m/$target/xms) {
$count_for{$next_book}++;
$is_available{$next_book} = 1;
}
my @events;
my @handlers;
my @unknowns;
# and later...
for my $event (@events) {
push @unknowns, grep { ! $_->handle($event) } @handlers;
}
print map { $_->err_msg } @unknowns;
2 注釋
所有注釋可用英文或中文書寫,盡量使用英文注釋。保持注釋和代碼的完全一致,修改程序時(shí),必須修改相應(yīng)的注釋。注釋的行數(shù)一般應(yīng)在程序總行數(shù)的1/5到1/3。禁止出現(xiàn)錯(cuò)別字。注釋應(yīng)該語義明確,避免出現(xiàn)二義性。
2.1 文件頭部注釋
每個(gè)含有源代碼的文件必須在文件開始有關(guān)于該文件的介紹性注釋。其中列出文件名、創(chuàng)建者、創(chuàng)建日期、功能描述、版本信息、版權(quán)聲明;如果對(duì)文件進(jìn)行了修改,應(yīng)該在文件頭中說明修改人、修改日期、修改原因,并變更文件的版本信息,最新版本信息放到最前面。
格式為:
#*********************************************************
# FileName: lucent_file_parser.pl
# Creator: Phonix <phonix@gmail.com.cn>
# Create Time: 2006-01-09
# Description: This is the proof-of-concept code for the
# Windows denial-of-serice attack described by
# the Razor team (NTBugtraq, 19-May-00).
# CopyRight: Copyright ? Bright Ocean Inter-Telecomm,All rights reserved.
# Revision: V1.0.0
# ModifyList:
# Revision: V1.1.1
# Modifier: Phonix
# ModifyTime: 2006-01-17
# ModifyReason: fix the bug of ……
#
# Revision: V1.1.0
# Modifier: Phonix
# ModifyTime: 2006-01-16
# ModifyReason: add mysql&oracle db support
#*********************************************************
不是簡(jiǎn)單的:
#========================================
#Created By:Phonix
#Created Time: 2006-01-09
#Modified Time: 2006-01-17
#========================================
2.2 文件中注釋
建議在文件中標(biāo)識(shí)出修改部分的起止位置。
正確:
# add mysql&oracle db support begin
……
# add mysql&oracle db support end
2.3 函數(shù)注釋
在每個(gè)函數(shù)前必須寫描述性注釋。說明這個(gè)函數(shù)的功能、參數(shù)和函數(shù)的返回值。
格式為:
#*******************************************************************
# Function Name: calc_time($datetime, $delta)
# Description: This function calculate the new datetime.
# Parameters:
# 1. $datetime is the base time, taking the format of 'yyyy-mm-dd hh:mm:ss'
# 2. $delta is the time which should be add to the $datetime. A positive value increase the time
# while the negative vale decrease the time
# Return:
# A new time string is returned, also taking the format of 'yyyy-mm-dd hh:mm:ss'
#*********************************************************************
不是簡(jiǎn)單的:
##########################################
#function name : get_ldap
#desc : get config info from ldap server
##########################################
2.4 程序塊注釋
程序塊注釋用于說明程序中的關(guān)鍵算法、使用某種技巧的代碼以及修改、測(cè)試、閱讀時(shí)必須加以注意的代碼。
格式為:
#*********************************************************************
#注釋內(nèi)容
#......
#......
#********************************************************************
2.5 語句注釋
用于對(duì)特定程序語句進(jìn)行說明,建議采取在語句行末尾說明的方法,同時(shí)注釋換行后也要對(duì)齊。格式為:
my @names = (
'Damian', # Primary key
# the key is
'Matthew', # Disambiguator
'Conway', # General class or category
);
3 代碼布局
3.1 括號(hào)
建議括號(hào)、插入語可以采用兩種方式之一,但是在一個(gè)程序里選定那種方式之后,那么要至始至終保持一致。
3.1.1 方式一
采用K&R風(fēng)格。
格式為:
my @names = (
'Damian', # Primary key
'Matthew', # Disambiguator
'Conway', # General class or category
);
for my $name (@names) {
for my $word ( anagrams_of(lc $name) ) {
print "$word\n";
}
}
3.1.2 方式二
括號(hào)對(duì){}對(duì)必須位于同一列,獨(dú)占一行,并且和{}之外的語句行對(duì)齊
my @names =
(
'Damian', # Primary key
'Matthew', # Disambiguator
'Conway', # General class or category
);
for my $name (@names)
{
for my $word (anagrams_of(lc $name))
{
print "$word\n";
}
}
3.2 關(guān)鍵字
把關(guān)鍵字和其他的內(nèi)容分開,關(guān)鍵字if, while, for,else…后面必須接一個(gè)空格。if和while語句必須使用’{‘和’}’括起語句體,即使只有一行代碼。建議不使用單行的if語句。
正確:
for my $result (@results) {
print_sep( );
print $result;
}
while ($min < $max) {
my $try = ($max - $min) / 2;
if ($value[$try] < $target) {
$max = $try;
}
else {
$min = $try;
}
}
if ($condition){
$i++;
}
不是:
for(@results) {
print_sep( );
print;
}
while($min < $max) {
my $try = ($max - $min) / 2;
if($value[$try] < $target) {
$max = $try;
}
else{
$min = $try;
}
}
if($condition) {$i++;}
3.3 子程序和變量
不要把子程序或變量和其后的括號(hào)部分分開。
正確:
my @candidates = get_candidates($marker);
CANDIDATE:
for my $i (0..$#candidates) {
next CANDIDATE if open_region($i);
$candidates[$i]
= $incumbent{ $candidates[$i]{region} };
}
不是:
my @candidates = get_candidates ($marker);
CANDIDATE:
for my $i (0..$#candidates) {
next CANDIDATE if open_region ($i);
$candidates [$i]
= $incumbent {$candidates [$i] {region}};
}
3.4 代碼縮排
縮進(jìn)采用四個(gè)空格,或一個(gè)TAB(1 TAB 設(shè)置成四個(gè)空格)。
3.5 代碼塊
不要把兩句話放在一行,每行只能寫一個(gè)語句。
正確:
while (my $record = <$inventory_file>) {
chomp $record;
next RECORD if $record eq $EMPTY_STR;
my @fields = split $FIELD_SEPARATOR, $record;
update_sales(\@fields);
$count++;
}
不是:
while (my $record = <$inventory_file>) {
chomp $record; next RECORD if $record eq $EMPTY_STR;
my @fields = split $FIELD_SEPARATOR, $record; update_sales(\@fields);$count++;
}
3.6 代碼長(zhǎng)度
每個(gè)函數(shù)體的語句行不能超過100行(不包括注釋,一個(gè)分號(hào)算一行)。每行長(zhǎng)度不要超過78個(gè)字符,超過該長(zhǎng)度時(shí),必須考慮換行,從低優(yōu)先級(jí)的操作符處分割長(zhǎng)表達(dá)式, 在賦值符前斷開長(zhǎng)的語句。
正確:
push (@steps, $steps[-1]
+ $radial_velocity * $elapsed_time
+ $orbital_velocity * ($phase + $phase_shift)
- $DRAG_COEFF * $altitude);
$predicted_val = $average + $predicted_change * $fudge_factor;
不是:
push (@steps, $steps[-1] + $radial_velocity
* $elapsed_time + $orbital_velocity
* ($phase + $phase_shift) - $DRAG_COEFF
* $altitude);
$predicted_val = $average
+ $predicted_change * $fudge_factor;
3.7 操作符
二元運(yùn)算符(算術(shù)運(yùn)算符, 賦值運(yùn)算符等)的兩邊都要接空格,低級(jí)操作符(如:+-)兩邊各有兩個(gè)空格,高級(jí)操作符(如:*%)兩邊各有一個(gè)空格。
在運(yùn)算符'->'兩邊不要使用空格,在一元操作符和操作數(shù)兩邊不要使用空格。
可以使用括號(hào)來表示運(yùn)算的先后順序。
正確:
my $displacement
= $initial_velocity * $time + 0.5 * $acceleration * $time**2;
my $price
= $coupon_paid * $exp_rate + ($face_val + $coupon_paid) * $exp_rate**2;
不是:
my $displacement=$initial_velocity*$time+0.5*$acceleration*$time**2;
my $price=$coupon_paid*$exp_rate+(($face_val+$coupon_val)*$exp_rate**2);
3.8 語句結(jié)束符
在每個(gè)語句后邊要填加分號(hào)。
正確:
while (my $line = <>) {
chomp $line;
if ( $line =~ s{\A (\s*) -- (.*)}{$1#$2}xms ) {
push @comments, $2;
}
print $line;
}
3.9 代碼排列
數(shù)組或Hash的賦值采用垂直排列。
正確:
my @months = qw(
January February March
April May June
July August September
October November December
);
my %expansion_of = (
q{it's} => q{it is},
q{we're} => q{we are},
q{didn't} => q{did not},
q{must've} => q{must have},
q{I'll} => q{I will},
);
不是:
my @months = qw(
January February March April May June July August September
October November December
);
my %expansion_of = (
q{it's} => q{it is}, q{we're} => q{we are}, q{didn't} => q{did not},
q{must've} => q{must have}, q{I'll} => q{I will},
);
3.10 非末端表達(dá)式
采用中間變量代替長(zhǎng)表達(dá)式。
正確:
my $next_step = $steps[-1]
+ $radial_velocity * $elapsed_time
+ $orbital_velocity * ($phase + $phase_shift)
- $DRAG_COEFF * $altitude
;
add_step( \@steps, $next_step, $elapsed_time);
不是:
add_step( \@steps, $steps[-1]
+ $radial_velocity * $elapsed_time
+ $orbital_velocity * ($phase + $phase_shift)
- $DRAG_COEFF * $altitude
, $elapsed_time);
3.11 功能塊順序
在每個(gè)Perl Scripts中,每個(gè)功能塊之間必須有一個(gè)空行。主程序?yàn)閙ain()函數(shù),功能塊出現(xiàn)順序如下:
use modules;
global variable 定義
main定義
sub routine 定義
4 數(shù)值和表達(dá)式
4.1 字符串界定符
需要用變量替換的字符串用雙引號(hào),否則用單引號(hào)。
正確:
my $spam_name = "$title $first_name $surname";
my $pay_rate = "$minimal for maximal work";
my $spam_name = 'Dr Lawrence Mwalle';
my $pay_rate = '$minimal for maximal work';
4.2 常量
使用字符常量,而不要直接用數(shù)值。
正確:
use Readonly;
Readonly my $MOLYBDENUM_ATOMIC_NUMBER => 42;
# and later...
print $count * $MOLYBDENUM_ATOMIC_NUMBER;
不是:
print $count * 42;
4.3 字符串
4.3.1 兩行
對(duì)于兩行的字符串,要用“.”進(jìn)行連接。
正確:
$usage = "Usage: $0 <file> [-full]\n"
. "(Use -full option for full dump)\n";
不是:
$usage = "Usage: $0 <file> [-full]
(Use -full option for full dump)";
4.3.2 多于兩行
對(duì)于多于兩行,要采用如下格式:
正確:
$usage = <<"END_USAGE";
Usage: $0 <file> [-full] [-o] [-beans]
Options:
-full : produce a full dump
-o : dump in octal
-beans : source is Java
END_USAGE
不是:
$usage = "Usage: $0 <file> [-full] [-o] [-beans]\n"
. "Options:\n"
. " -full : produce a full dump\n"
. " -o : dump in octal\n"
. " -beans : source is Java\n"
;
4.4 哈希變量
Hash變量的定義采用雙箭頭(=>)方式。
正確:
%default_service_record = (
name=> '<unknown>',
rank=> 'Recruit',
serial=> undef,
unit=> ['Training platoon'],
duty=> ['Basic training'],
);
不是:
%default_service_record = (
'name', '<unknown>',
'rank', 'Recruit',
'serial', undef,
'unit', ['Training platoon'],
'duty', ['Basic training'],
);
5 函數(shù)
5.1 調(diào)用語法
調(diào)用時(shí)要使用圓括號(hào),不管是否有參數(shù)。
正確:
fix();
coerce($input, $INTEGER, $ROUND_ZERO);
不是:
fix;
5.2 函數(shù)返回
在函數(shù)中要進(jìn)行顯式的return返回。
正確:
sub set_terseness {
my ($terseness) = @_;
my $default_terseness = $terseness;
return; # Explicitly return nothing meaningful
}
不是:
sub set_terseness {
my ($terseness) = @_;
my $default_terseness = $terseness;
}
6 編程慣例
6.1 使用use strict
所有Perl Scripts 文件中必須在開始使用“use strict;”,進(jìn)行嚴(yán)格的語法檢查,便于查找錯(cuò)誤。
6.2 避免使用內(nèi)部變量名稱
避免使用Perl內(nèi)部變量。使用“use English;”裝入Perl內(nèi)部變量的符號(hào)名稱。使用my來限定變量的作用域。下面是一些Perl內(nèi)部變量名稱的對(duì)應(yīng)關(guān)系。
$_ $ARG
@_ @ARG
$! $ERRNO
$? $CHILD_ERROR
$$ $PID
$0 $PROGRAM_NAME
$. $INPUT_LINE_NUMBER
$| $OUTPUT_AUTOFLUSH
$@ $EVEL_ERROR
$& $MATCH
$` $PREMATCH
$' $POSTMATCH
$+ $LAST_PAREN_MATCH
$/ $RS
$\ $ORS
$< $UID
$> $EUID
$( $GID
$) $EGID
$] $PERL_VERSION
$? $CHILD_ERROR
6.3 避免使用goto
避免使用goto語句(只有在從多重循環(huán)的內(nèi)部跳出時(shí)才可以使用)。除非能夠特別有效的增加程序的效率并且不影響程序良好結(jié)構(gòu)的特殊情況。
6.4 語法檢查(輔助工具)
使用-cw選項(xiàng)檢查Perl程序的語法。
正確:
perl -cw -Mdiagnostics file.pl # check syntax with warnings on
nt; # Perl 5.004_04, define constants
use Env; # instead of $ENV{'HOME'}; Shortens the usage, but do not
# mix normal variables
# with environment variables.
6.5 use Carp; # For modules: 使用標(biāo)準(zhǔn)模塊
盡量使用標(biāo)準(zhǔn)庫函數(shù)、公共函數(shù)和開發(fā)庫中已有的函數(shù)和模塊,使用FileHandle模塊來處理文件的讀寫。盡量使用以下的標(biāo)準(zhǔn)Perl模塊:
use strict; # helps you to locate syntax errors or uncertainties.
use integer; # if you don't need floating point math,it will speed Perl up.
use constagives you `carp' and `croak'
use English; # gives symbolic names, like $! ==> $ERRNO
use Getopt::Long; # --posix command line option handling
use Cwd; # platform independent cwd()
use File::Basename; # don't invent your own wheel of this.
use File::Find; # don't use system("find . -name ...")...
use File::copy; # don't use system("cp this that");
use File::patch; # instead of system("mkdir");
use File::stat; # readable: $st = stat($file), $st->mode
use DirHandle; # OO form of `readdir'
use Text::Tabs # un/expand tabs in text
use Text::ParseWords; # Parse text into tokens, understands embedded
# quotes. @a = "ewords("[ +]", 0, $_);
# a+b, "a b" + c
use Socket; # socket handling
use Sys::Hostname; # don't invent your own wheel
use Net::Ping # unix ping, check if host is online
use Time::Local # time manipulations
6.6 其他
1、編碼、測(cè)試、注釋,是程序員的三項(xiàng)基本工作,它們是同等重要的。
2、可靠性第一,可讀性第二,效率第三。只有在極個(gè)別必須強(qiáng)調(diào)效率的部分,可以是可靠性第一,效率第二,可讀性第三。
3、首先是正確,其次是優(yōu)美。
4、無法證明你的程序沒有錯(cuò)誤。因此,在新編寫完一段程序后,應(yīng)該測(cè)試通過后再繼續(xù)編碼。
5、改正一個(gè)錯(cuò)誤的同時(shí),可能會(huì)引起新的錯(cuò)誤。因此,在修改bug前,首先考慮對(duì)其他程序的影響。修改后,應(yīng)該對(duì)程序進(jìn)行完整的測(cè)試,而不是只對(duì)修改部分進(jìn)行測(cè)試。
6、避免使用很多個(gè)參數(shù)的函數(shù)。
7、函數(shù)應(yīng)該只有一個(gè)出口。
8、循環(huán)應(yīng)該只有一個(gè)出口,避免多個(gè)出口。
9、盡量避免使用全局變量。
10、在盡可能小的作用域內(nèi)定義和使用變量。
11、使用括號(hào),表達(dá)復(fù)雜表達(dá)式中的操作符的優(yōu)先順序。
12、循環(huán)、分支不要超過五個(gè)層次。
13、循環(huán)、分支等語句后,即使只有一行代碼時(shí),也要使用{}將其括起來。
14、禁止else goto和else return。
15、重復(fù)使用的、完成相對(duì)獨(dú)立功能的算法、代碼,應(yīng)該抽象為公共模塊。