星期五, 八月 31, 2007

FireFox Add-ons: Live HTTP Headers

Introduction:
With this Add-on, you can monitor HTTP Headers both FireFox send and receive.

DownLoad:

Usage:
Tools --> Live HTTP Headers

FireFox Add-ons: IE Tab

Introduction:
With IE Tab, you can open  page  with  IE  in  FireFox.
You can download it form:

Usage:
1.Tools --> IE Tab Options
Here you can set which site you want to open using ietab.

2.Status Bar Icon:
Click on the icon, then FireFox will change the browser's engine.

星期三, 八月 29, 2007

使用程序申请900号的户籍证明

使用的是 Python + Curl 的组合,

Python脚本用来写控制程序,网络部分由 Curl 完成。

curl 是一个网络应用程序,使用很方便,支持http ftp https telnet 等众多协议,
还有curllib,又很多语言的Bind,常见的和不常见的语言都支持,
有:
Ada95                     OCaml            
Basic                     Pascal           
C                         Perl             
C++                       PHP              
Ch                        Postgres         
Cocoa                     Python           
D                         R                
Dylan                     Rexx             
Euphoria                  Ruby             
Ferite                    Scheme           
Gambas                    S-Lang           
glib/GTK+                 Smalltalk        
Java                      SPL              
Lisp                      Tcl              
Lua                       Visual Basic     
Mono                      Q                
.NET                      wxWidgets        
Object-Pascal             XBLite           
                                  
还使用到了FireFox和它的Live HTTP Header插件,来得到HTTP Post的内容。

工具有了,讲一下怎么实现的:

HTTP POST:
curl.exe -d "post head" http://firstjob.com.cn/gic_web/residentcertificateapplymain.jsp > huji
其中post的内容是通过FireFox的Live HTTP Header插件得到的

控制:
控制是使用Python的,算法这样:
在23点以后,用os.system(cmd) 运行上面的CMD,
之后检查输出文件是否含有字符串 "今天的申请分额已满"
如果有,则等待30秒后重试,
如果没有,则认为成功,退出

程序下载 http://gudonghua.googlepages.com/residentcertificateapply.html

星期二, 八月 28, 2007

通信控件MSComm使用详解

1.MSComm控件两种处理通讯的方式

  MSComm控件提供下列两种处理通讯的方式:事件驱动方式和查询方式。

  1.1 事件驱动方式

   事件驱动通讯是处理串行端口交互作用的一种非常有效的方法。在许多情况下,在事件发生时需要得到通知,例如,在串口接收缓冲区中有字符,或者 Carrier Detect (CD) Request To Send (RTS) 线上一个字符到达或一个变化发生时。在这些情况下,可以利用 MSComm 控件的 OnComm 事件捕获并处理这些通讯事件。OnComm 事件还可以检查和处理通讯错误。所有通讯事件和通讯错误的列表,参阅 CommEvent 属性。在编程过程中,就可以在OnComm事件处理函数中加入自己的处理代码。这种方法的优点是程序响应及时,可靠性高。每个MSComm 控件对应着一个串行端口。如果应用程序需要访问多个串行端口,必须使用多个 MSComm 控件。

  1.2 查询方式

   查询方式实质上还是事件驱动,但在有些情况下,这种方式显得更为便捷。在程序的每个关键功能之后,可以通过检查 CommEvent 属性的值来查询事件和错误。如果应用程序较小,并且是自保持的,这种方法可能是更可取的。例如,如果写一个简单的电话拨号程序,则没有必要对每接收一个字 符都产生事件,因为唯一等待接收的字符是调制解调器的 "确定"响应。

  2.MSComm 控件的常用属性

  MSComm 控件有很多重要的属性,但首先必须熟悉几个属性。

CommPort

设置并返回通讯端口号。

Settings

以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位。

PortOpen

设置并返回通讯端口的状态。也可以打开和关闭端口。

Input

从接收缓冲区返回和删除字符。

Output

向传输缓冲区写一个字符串。


  下面分别描述:

  CommPort属性:设置并返回通讯端口号。

  语法 object.CommPort[value ] (value 一整型值,说明端口号。)

  说明 在设计时,value 可以设置成从 1 16 的任何数(缺省值为 1)。但是如果用 PortOpen 属性打开一个并不存在的端口时,MSComm 控件会产生错误 68(设备无效)。

  注意:必须在打开端口之前设置 CommPort 属性。

  RThreshold 属性:在 MSComm 控件设置 CommEvent 属性为 comEvReceive 并产生 OnComm 之前,设置并返回的要接收的字符数。

  语法:object.Rthreshold [ = value ]value 整型表达式,说明在产生 OnComm 事件之前要接收的字符数。

  说明:当接收字符后,若 Rthreshold 属性设置为 0(缺省值)则不产生 OnComm 事件。例如,设置 Rthreshold 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 OnComm 事件。

  CTSHolding 属性:确定是否可通过查询 Clear To Send (CTS) 线的状态发送数据。Clear To Send 是调制解调器发送到相联计算机的信号,指示传输可以进行。该属性在设计时无效,在运行时为只读。

  语法: object.CTSHoldingBoolean

  Mscomm 控件的 CTSHolding 属性设置值:

  True Clear To Send 线为高电平。
  False Clear To Send 线为低电平。

   说明:如果 Clear To Send 线为低电平 (CTSHolding = False) 并且超时时,MSComm 控件设置 CommEvent 属性为 comEventCTSTO (Clear To Send Timeout) 并产生 OnComm 事件。

  Clear To Send 线用于 RTS/CTS (Request To Send/Clear To Send) 硬件握手。如果需要确定 Clear To Send 线的状态,CTSHolding 属性给出一种手工查询的方法。

  详细信息 有关握手协议,请参阅 Handshaking 属性。

  SThreshold 属性 MSComm 控件设置 CommEvent 属性为 comEvSend 并产生 OnComm 事件之前,设置并返回传输缓冲区中允许的最小字符数。

  语法 object.SThreshold [ = value ]

  value 整形表达式,代表在 OnComm 事件产生之前在传输缓冲区中的最小字符数。

   说明:若设置 Sthreshold 属性为 0 (缺省值),数据传输事件不会产生 OnComm 事件。若设置 Sthreshold 属性为 1,当传输缓冲区完全空时,MSComm 控件产生 OnComm 事件。如果在传输缓冲区中的字符数小于 valueCommEvent 属性设置为 comEvSend,并产生 OnComm 事件。comEvSend 事件仅当字符数与 Sthreshold 交叉时被激活一次。例如,如果 Sthreshold 等于 5,仅当在输出队列中字符数从 5 降到 4 时,comEvSend 才发生。如果在输出队列中从没有比 Sthreshold 多的字符,comEvSend 事件将绝不会发生。

  Handshake 常数

常数

描述

comNone

0

无握手。

comXonXoff

1

XOn/Xoff 握手。

comRTS

2

Request-to-send/clear-to-send 握手。

comRTSXOnXOff

3

Request-to-send clear-to-send 握手皆可。


  OnComm 常数

常数

描述

comEvSend

1

发送事件。

comEvReceive

2

接收事件。

comEvCTS

3

clear-to-send 线变化。

comEvDSR

4

data-set ready 线变化。

comEvCD

5

carrier detect 线变化。

comEvRing

6

振铃检测。

comEvEOF

7

文件结束。


  Error 常数

常数

描述

comEventBreak

1001

接收到中断信号

comEventCTSTO

1002

Clear-to-send 超时

comEventDSRTO

1003

Data-set ready 超时

comEventFrame

1004

帧错误

comEventOverrun

1006

端口超速

comEventCDTO

1007

Carrier detect 超时

comEventRxOver

1008

接收缓冲区溢出

comEventRxParity

1009

Parity 错误

comEventTxFull

1010

传输缓冲区满

comEventDCB

1011

检索端口 设备控制块 (DCB) 时的意外错误


  InputMode 常数

常数

描述

comInputModeText

0 (缺省)

通过 Input 属性以文本方式取回数据。

comInputModeBinary

1

通过 Input 属性以二进制方式检取回数据。


  CDHolding 属性:通过查询 Carrier Detect (CD) 线的状态确定当前是否有传输。Carrier Detect 是从调制解调器发送到相联计算机的一个信号,指示调制解调器正在联机。该属性在设计时无效,在运行时为只读。

  语法 object.CDHolding

  设置值:CDHolding 属性的设置值为:

设置

描述

True

Carrier Detect 线为高电平

False

Carrier Detect 线为低电平


  说明:注意当 Carrier Detect 线为高电平 (CDHolding = True) 且超时时,MSComm 控件设置CommEvent 属性为 comEventCDTOCarrier Detect 超时错误),并产生 OnComm 事件。

  注意 在主机应用程序中捕获一个丢失的传输是特别重要的,例如一个公告板,因为呼叫者可以随时挂起(放弃传输)。

  Carrier Detect 也被称为 Receive Line Signal Detect (RLSD)

  数据类型:Boolean

  DSRHolding 属性:确定 Data Set Ready (DSR) 线的状态。Data Set Ready 信号由调制解调器发送到相连计算机,指示作好操作准备。该属性在设计时无效,在运行时为只读。

  语法:object.DSRHolding

  object 所在处表示对象表达式,其值是"应用于" 列表中的对象。

  DSRHolding 属性返回以下值:

描述

说明

True

Data Set Ready 线高

  当 Data Set Ready 线为高电平 (DSRHolding = True) 且超时时,MSComm 控件设置 CommEvent 属性为 comEventDSRTO(数据准备超时)并产生 OnComm 事件。
  当为 Data Terminal Equipment (DTE) 机器写 Data Set Ready/Data Terminal Ready 握手例程时该属性是十分有用的。
  数据类型:Boolean

False

Data Set Ready 线低


  Settings 属性: 设置并返回波特率、奇偶校验、数据位、停止位参数。

  语法: object.Settings[ = value]

  说明:当端口打开时,如果 value 非法,则 MSComm 控件产生错误 380 (非法属性值)。

  Value 由四个设置值组成,有如下的格式:

  "BBBB,P,D,S"

  BBBB 为波特率,P 为奇偶校验,D 为数据位数,S 为停止位数。value 的缺省值是:

  "9600,N,8,1"

  InputLen 属性:设置并返回 Input 属性从接收缓冲区读取的字符数。

  语法 object.InputLen [ = value]

  InputLen 属性语法包括下列部分:

  value 整型表达式,说明 Input 属性从接收缓冲区中读取的字符数。

  说明:InputLen 属性的缺省值是 0。设置 InputLen 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容。

   若接收缓冲区中 InputLen 字符无效,Input 属性返回一个零长度字符串 ("")。在使用 Input 前,用户可以选择检查 InBufferCount 属性来确定缓冲区中是否已有需要数目的字符。该属性在从输出格式为定长数据的机器读取数据时非常有用。

  EOFEnable 属性:确定在输入过程中 MSComm 控件是否寻找文件结尾 (EOF) 字符。如果找到 EOF 字符,将停止输入并激活 OnComm 事件,此时 CommEvent 属性设置为 comEvEOF

  语法:object.EOFEnable [ = value ]

  EOFEnable 属性语法包括下列部分:

  value 布尔表达式,确定当找到 EOF 字符时,OnComm 事件是否被激活,如"设置值"中所描述。

  value 的设置值:

  True EOF 字符找到时 OnComm 事件被激活。

  False (缺省)当 EOF 字符找到时 OnComm 事件不被激活。

  说明:当 EOFEnable 属性设置为 FalseOnComm 控件将不在输入流中寻找 EOF 字符。

  3.错误消息(MS Comm 控件)

  下表列出 MSComm 控件可以捕获的错误:

描述

380

无效属性值 comInvalidPropertyValue

383

属性为只读 comSetNotSupported

394

属性为只读 comGetNotSupported

8000

端口打开时操作不合法 comPortOpen

8001

超时值必须大于 0

8002

无效端口号 comPortInvalid

8003

属性只在运行时有效

8004

属性在运行时为只读

8005

端口已经打开 comPortAlreadyOpen

8006

设备标识符无效或不支持该标识符

8007

不支持设备的波特率

8008

指定的字节大小无效

8009

缺省参数错误

8010

硬件不可用(被其它设备锁定)

8011

函数不能分配队列

8012

设备没有打开 comNoOpen

8013

设备已经打开

8014

不能使用 comm 通知

8015

不能设置 comm 状态 comSetCommStateFailed

8016

不能设置 comm 事件屏蔽

8018

仅当端口打开时操作才有效 comPortNotOpen

8019

设备忙

8020

comm 设备错误 comReadError

8021

为该端口检索设备控制块时的内部错误 comDCBError

 

Vim tab (标签) 的使用

标签(tab)是 Vim7的新功能,

编辑多个文件时就不用开多个Vim了,

也不用每次想着保存再n了。




常用命令:



命令 说明 快捷键
:tab new 新建一个标签页
:tabedit 在新标签页打开文件
:tabnext 下一个标签页 <C-PageDown>
:tabp[revious] 上一个标签页 <C-PageUp>

星期一, 八月 27, 2007

MSComm控件License安装

要用VB写一个串口通讯的小程序,
用到MSComm控件,
可是装好VB后又发现需要License。
上网查了一下,只需要做如下修改就好了。

把下面这段代码写成1.REG文件,然后运行1.reg,注册就可以了:

REGEDIT4

[HKEY_CLASSES_ROOT\Licenses\4250E830-6AC2-11cf-8ADB-00AA00C00905]  
@="kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun"

星期六, 八月 25, 2007

Vim 新书快递

Hacking Vim: A Cookbook to get the Most out of the Latest Vim Editor


看了一下介绍,讲的是Vim7.0,最新的版本,但是不适合初学者,对有一定Vim基础的应该是一本比较好的书,还没有下载完,一会先去浏览一下,看介绍的,我对一些章节还是挺感兴趣的,比如:auto-completion 和 folding。

下面是书的介绍:


This cookbook contains ready-to-use hacks to solve problems Vim users encounter daily, from personalizing Vim to optimizations that boost productivity. It does not cover basic use of the editor but focuses on making life easier for experienced Vim users. Vim is a highly configurable, open-source, multi-platform text editor that is included as standard in most Linux distributions. It can edit code in any language, has a scripting language that allows extensions to its functionality, and is editor of choice for many programmers. This book is up to date with the new features in Vim 7.0. Chapters cover: changing the appearance of the Vim editor; improved file and buffer navigation ; using templates, auto-completion, folding, sessions, and registers; formatting text and code and using external formatting scripts; Vim scripts and scripting. Each recipe has a self-contained description of the task it covers, how to use it, the benefits of using it, and compatibility with earlier versions of Vim.





Back in the early days of the computer revolution, system resources were limited and developers had to figure out new ways to optimize their applications. This was also the case with the text editors of that time. One of the most popular editors of that time was an editor called Vim. It was optimized to near-perfection for the limited system resources on which it ran.





The world has come a long way since then, and even though the system resources have grown, many still stick with the Vim editor.





At first sight, the Vim editor might not look like much. However, if you look beneath the simple user-interface, you will discover why this editor is still the favorite editor for so many people, even today!
This editor has nearly every feature you would ever want, and if it's not in the editor, it is possible to add it by creating plugins and scripts. This high level of flexibility makes it ideal for many purposes, and it is also why Vim is still one of the most advanced editors.





New users join the Vim user community every day and want to use this editor in their daily work, and even though Vim sometimes can be complex to use, they still favor it above other editors. This is a book for these Vim users.





With this book, Vim users can make their daily work in the editor more comfortable and thereby optimize their productivity. In this way they will not only have an optimized editor, but also an optimized work-flow. The book will help them move from just using Vim as a simple text editor to a situation where they feel at home and can use it for many of their daily tasks.





Good luck and happy reading!

星期五, 八月 24, 2007

upvar: point in tcl

When we need pass a variable name to a procedure, we can use upvar. upvar like reference in C++, we can changes the variable as we like.

the syntax of upvar is:
upvar ?level? varName localvar 
example:

#!/usr/bin/tcl
proc swap { x y} {
    upvar x a
    upvar y b
    set tmp  $a
    set a    $b
    set b    $tmp
}

set x 10
set y 5
swap x y
puts "x is $x"
puts "y is $y"  

result:
x is 5
y is 10

星期四, 八月 23, 2007

tcl 中array做为函数参数传递的调用方法

现在一般教本语言(perl python tcl)中都会提供字典(数组)这样的数据结构,
这种结构使用起来最为灵活方便,但是也最不容易掌握。

在TCL中可以将数组名作为函数参数,再通过upvar,得到数组的内容。
下面的例子就显示了这个用法:

#!/usr/bin/tclsh

proc foo {arr_foo} {
    upvar $arr_foo arr
    puts -nonewline {$arr(key1):}
    puts $arr(key1)
    puts -nonewline {$arr(key2):}
    puts $arr(key2)
}

set a(key1) value1
set a(key2) value2
foo a       
  
结果:
$arr(key1):value1
$arr(key2):value2



tcl regsub bug

 1 tcl>set drv  5
2 tcl>set foo "oai21_e5m_hvt"
3 tcl>regsub {_(_v\d)?e(\d+)} $foo "_ \1 e$drv" goo
4 1
5 tcl>echo $goo
6 oai21_e5m_hvt
7 tcl>echo $foo
8 oai21_e5m_hvt
9 tcl> string match $foo $goo
10 0
11 tcl>string length $foo
12 13
13 tcl>string length $goo
14 14
15
tcl>string index $goo 6
16
17
tcl>string index $foo 6
18 e
19 tcl>


Reason:

the problem is that because of the 's the \1 is being translated into hex 0x01 or the SOH char

they need to \ the \1 or \\1


the following is the right


regsub {_(_v\d)?e(\d+)} $foo "_\\1e$drv" goo

星期三, 八月 22, 2007

FireFox CHM 插件

这个插件可以使firefox直接打开CHM文件。
下载地址:
https://addons.mozilla.org/en-US/firefox/addon/3235

使用方法:
1、 File  -> Open Chm File
2、View -> Sidebar -> CHM Reader

星期二, 八月 21, 2007

The Zen of Python (Python之神)

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one—and preferably only one—obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than  right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea—let's do more of those!

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

A Good Idea Is Always Simple And Easy For Understand.


星期一, 八月 20, 2007

Python 编码规范

== 介绍 ==

    这篇文档所给出的编码约定适用于在主要的Python发布版本中组成标准库的Python
    代码.请查阅相关的关于在Python的C实现中C代码风格指南的描述.

    这篇文档改编自Guido最初的《Python风格指南》一文.
    并从《Barry's style guide》中添加了部分内容.
    在有冲突的地方,Guide的风格规则应该是符合本PEP的意图
    (译注:就是当有冲突时,应以Guido风格为准)
    这篇PEP也许仍然尚未完成(实际上,它可能永远不会结束).

== 一致性的建议 ==
'''愚蠢得使用一致性是无知的妖怪(A Foolish Consistency is the Hobgoblin of Little Minds)'''
{{{呆板的坚持一致性是傻的没边了!
-- Zoomq}}}


    在这篇风格指导中的一致性是重要的.
    在一个项目内的一致性更重要.
    在一个模块或函数内的一致性最重要.


    但最重要的是:知道何时会不一致 -- 有时只是没有实施风格指导.当出现疑惑时,
     运用你的最佳判断.看看别的例子,然后决定怎样看起来更好.并且要不耻下问!

 * 打破一条既定规则的两个好理由:

  1. 当应用这个规则是将导致代码可读性下降,即便对某人来说,他已经习惯于按这条规则来阅读代码了.
  1. 为了和周围的代码保持一致而打破规则(也许是历史原因)
     * -- 虽然这也是个清除其它混乱的好机会(真正的XP风格).



== 代码的布局 ==
(Code lay-out)
=== 缩进 ===
(Indentation)


    使用Emacs的Python-mode的默认值:4个空格一个缩进层次.
    对于确实古老的代码,你不希望产生混乱,可以继续使用8空格的制表符(8-space tabs).
    Emacs Python-mode自动发现文件中主要的缩进层次,依此设定缩进参数.

=== 制表符还是空格? ===
(Tabs or Spaces)


    永远不要混用制表符和空格. 最流行的Python缩进方式是仅使用空格,
    其次是仅使用制表符.混合着制表符和空格缩进的代码将被转换成仅使用空格.
    (在Emacs中,选中整个缓冲区,按ESC-x去除制表符(untabify).)
    调用python命令行解释器时使用-t选项,可对代码中不合法得混合制表符和空格发出警告(warnings).
    使用-tt时警告(warnings)将变成错误(errors).这些选项是被高度推荐的.



    对于新的项目,强烈推荐仅使用空格(spaces-only)而不是制表符.
    许多编辑器拥有使之易于实现的功能.(在Emacs中,确认indent-tabs-mode是nil).

=== 行的最大长度 ===
(Maximum Line Length)


    周围仍然有许多设备被限制在每行80字符;而且,窗口限制在80个字符
    使将多个窗口并排放置成为可能.在这些设备上使用默认的折叠(wrapping)方式看起来有点丑陋.
    因此,请将所有行限制在最大79字符(Emacs准确得将行限制为长80字符),
    对顺序排放的大块文本(文档字符串或注释),推荐将长度限制在72字符.



    折叠长行的首选方法是使用Pyhon支持的圆括号,方括号(brackets)和花括号(braces)内的行延续.
    如果需要,你可以在表达式周围增加一对额外的圆括号,
    但是有时使用反斜杠看起来更好.确认恰当得缩进了延续的行.
    Emacs的Python-mode正确得完成了这些.一些例子:
{{{
#!python
    class Rectangle(Blob):

        def __init__(self, width, height,
                     color='black', emphasis=None, highlight=0):
            if width == 0 and height == 0 and \
               color == 'red' and emphasis == 'strong' or \
               highlight > 100:
                raise ValueError, "sorry, you lose"
            if width == 0 and height == 0 and (color == 'red' or
                                               emphasis is None):
                raise ValueError, "I don't think so"
            Blob.__init__(self, width, height,
                          color, emphasis, highlight)
}}}
=== 空行 ===
(Blank Lines)


    用两行空行分割顶层函数和类的定义,类内方法的定义用单个空行分割.
    额外的空行可被用于(保守的(sparingly))分割相关函数组成的群(groups of related functions).
    在一组相关的单句中间可以省略空行.(例如.一组哑元(a set of dummy
    implementations)).



    当空行用于分割方法(method)的定义时,在'class'行和第一个方法定义之间也要有一个空行.


    在函数中使用空行时,请谨慎的用于表示一个逻辑段落(indicate logical sections).


    Python接受contol-L(即^L)换页符作为空格;Emacs(和一些打印工具)
    视这个字符为页面分割符,因此在你的文件中,可以用他们来为相关片段(sections)分页.

=== 编码 ===
(Encodings)[wiki:epes/pep- 0263.html (PEP 263)]


    Python核心发布中的代码必须始终使用ASCII或Latin-1编码(又名 ISO-8859-1).
    使用ASCII的文件不必有译码cookie(coding cookie).
    Latin-1仅当注释或文档字符串涉及作者名字需要Latin-1时才被使用;
    另外使用\x转义字符是在字符串中包含非ASCII(non-ASCII)数据的首选方法.
    作为PEP 263实现代码的测试套件的部分文件是个例外.

{{{Python 2.4 以后内核支持 Unicode 了!
不论什么情况使用 UTF-8 吧!这是王道!
}}}--ZoomQuiet



== 导入 ==
(Imports)

 * 通常应该在单独的行中导入(Imports),例如:
{{{
        No:  import sys, os
        Yes: import sys
             import os
}}}
      但是这样也是可以的:
{{{
        from types import StringType, ListType
}}}

 * Imports 通常被放置在文件的顶部,仅在模块注释和文档字符串之后,在模块的全局变量和常量之前.Imports应该有顺序地成组安放 .

  1. 标准库的导入(Imports )
  1. 相关的主包(major package)的导入(即,所有的email包在随后导入)
  1. 特定应用的导入(imports)

 * 你应该在每组导入之间放置一个空行.
 * 对于内部包的导入是不推荐使用相对导入的.对所有导入都要使用包的绝对路径.
 * 从一个包含类的模块中导入类时,通常可以写成这样:
{{{
        from MyClass import MyClass
        from foo.bar.YourClass import YourClass
}}}
      如果这样写导致了本地名字冲突,那么就这样写
{{{
        import MyClass
       import foo.bar.YourClass
}}}
 * 即使用{{{"MyClass.MyClass"}}}和{{{"foo.bar.YourClass.YourClass "}}}
== 空格 ==
(Whitespace in Expressions and Statements)

    Guido不喜欢在以下地方出现空格:

  {{{"spam( ham[ 1 ], { eggs: 2 } )".  Always write this as}}}
  {{{"spam(ham[1], {eggs: 2})".}}}

    * 紧挨着圆括号,方括号和花括号的,如:{{{"spam( ham[ 1 ], { eggs: 2 } )".}}}
  要始终将它写成{{{"spam(ham[1], {eggs: 2})".}}}

  {{{"if x == 4 : print x , y ; x , y = y , x".}}}  Always write this as
  {{{"if x == 4: print x, y; x, y = y, x".}}}

    * 紧贴在逗号,分号或冒号前的,如:
  {{{"if x == 4 : print x , y ; x , y = y , x".}}}  要始终将它写成
  {{{"if x == 4: print x, y; x, y = y, x".}}}

    * 紧贴着函数调用的参数列表前开式括号(open parenthesis )的,如{{{"spam (1)"}}}.要始终将它写成{{{"spam(1)"}}}.

  {{{slicing, as in: "dict ['key'] = list [index]".}}}  Always
  write this as {{{"dict['key'] = list[index]".}}}

    *紧贴在索引或切片(slicing?下标?)开始的开式括号前的,如:
  {{{"dict ['key'] = list [index]".要始终将它写成"dict['key'] = list[index]".}}}

    * 在赋值(或其它)运算符周围的用于和其它并排的一个以上的空格,如:
{{{
#!python
          x             = 1
          y             = 2
          long_variable = 3
}}}
      要始终将它写成
{{{
#!python
         x = 1
         y = 2
         long_variable = 3
}}}

    (不要对以上任意一条和他争论 --- Guido 养成这样的风格超过20年了.)

=== 其它建议 ===
(Other Recommendations)

 * 始终在这些二元运算符两边放置一个空格:赋值(=), 比较(==, <, >, !=, <>, <=,>=, in, not in, is, is not), 布尔运算 (and, or, not).


* 按你的看法在算术运算符周围插入空格. 始终保持二元运算符两边空格的一致.
 * 一些例子:
{{{
#!python
          i = i+1
          submitted = submitted + 1
          x = x*2 - 1
          hypot2 = x*x + y*y
          c = (a+b) * (a-b)
          c = (a + b) * (a - b)
}}}
    * 不要在用于指定关键字参数或默认参数值的'='号周围使用空格,例如:
{{{
#!python
          def complex(real, imag=0.0):
              return magic(r=real, i=imag)
}}}
    * 不要将多条语句写在同一行上.
{{{
          No:  if foo == 'blah': do_blah_thing()
          Yes: if foo == 'blah':
                   do_blah_thing()

          No:  do_one(); do_two(); do_three()
          Yes: do_one()
               do_two()
               do_three()
}}}
== 注释 ==
(Comments)


    同代码不一致的注释比没注释更差.当代码修改时,始终优先更新注释!



    注释应该是完整的句子. 如果注释是一个短语或句子,首字母应该大写,
    除非他是一个以小写字母开头的标识符(永远不要修改标识符的大小写).



    如果注释很短,最好省略末尾的句号(period?结尾句末的停顿?也可以是逗号吧,)
    注释块通常由一个或多个由完整句子构成的段落组成,每个句子应该以句号结尾.



    你应该在句末,句号后使用两个空格,以便使Emacs的断行和填充工作协调一致
    (译按:应该说是使这两种功能正常工作,".  "给出了文档结构的提示).


    用英语书写时,断词和空格是可用的.


    非英语国家的Python程序员:请用英语书写你的注释,除非你120%的确信
    这些代码不会被不懂你的语言的人阅读.

{{{我就是坚持全部使用中文来注释,真正要发布脚本工具时,再想E文的;
开发时每一瞬间都要用在思量中,坚决不用在E文语法,单词的回忆中!
}}}-- ZoomQUiet
 * 约定使用统一的文档化注释格式有利于良好习惯和团队建议!-- CodeCommentingRule

=== 注释块 ===
(Block Comments)

    注释块通常应用于跟随着一些(或者全部)代码并和这些代码有着相同的缩进层次.
    注释块中每行以'#'和一个空格开始(除非他是注释内的缩进文本).
    注释块内的段落以仅含单个'#'的行分割.
    注释块上下方最好有一空行包围(或上方两行下方一行,对一个新函数定义段的注释).

=== 行内注释 ===
(Inline Comments)
 * (inline?内联?翻成"行内"比较好吧)


    一个行内注释是和语句在同一行的注释.行内注释应该谨慎适用.
    行内注释应该至少用两个空格和语句分开.
    它们应该以'#'和单个空格开始.

{{{
        x = x+1                 # Increment x
}}}
    如果语意是很明了的,那么行内注释是不必要的,事实上是应该被去掉的.
    不要这样写:
{{{
        x = x+1                 # Increment x
}}}
{{{
        x = x+1                 # Compensate for border
}}}
    但是有时,这样是有益的:
{{{
        x = x+1                 # Compensate for border
}}}


== 文档化 ==
(Documentation Strings)

    Conventions for writing good documentation strings
    (a.k.a. "docstrings") are immortalized in
    [wiki:epes/pep-0257.htm PEP 257].

    应该一直遵守编写好的文档字符串(又名"docstrings")的约定(?实在不知道怎么译)
{{{Documentation Strings-- 文档化字符 ;
为配合 pydoc;epydoc,Doxygen等等文档化工具的使用,类似于MoinMoin 语法,约定一些字符,
以便自动提取转化为有意义的文档章节等等文章元素!
-- Zoomq}}}


    * 为所有公共模块,函数, 类和方法编写文档字符串.文档字符串对非公开的方法不是必要的,但你应该有一个描述这个方法做什么的注释.这个注释应该在"def"这行后.


    * [wiki:epes/pep-0257.htm PEP 257] 描述了好的文档字符串的约定.一定注意,多行文档字符串结尾的"""
    应该单独成行,例如:
{{{
      """Return a foobang

      Optional plotz says to frobnicate the bizbaz first.
      """
}}}

    * 对单行的文档字符串,结尾的"""在同一行也可以.

{{{实际上Python 自个儿就使用文档化编码维护着所有内置对象的使用说明\
不信的话常试:
    #python
>>> import time
>>> dir(time)
['__doc__', '__file__', '__name__', 'accept2dyear', 'altzone', 'asctime', 'clock', 'ctime', 'daylight', 'gmtime', 'localtime', 'mktime', 'sleep', 'strftime', 'strptime', 'struct_time', 'time', 'timezone', 'tzname', 'tzset']
>>> help(time.time)
Help on built-in function time in module time:

time(...)
    time() -> floating point number

    Return the current time in seconds since the Epoch.
    Fractions of a second may be present if the system clock provides them.

}}}
== 版本注记 ==
(Version Bookkeeping)
(我觉得叫"注记"更好)


    如果你要将RCS或CVS的杂项(crud)包含在你的源文件中,按如下做.
{{{
#!python
        __version__ = "$Revision: 1.4 $"
        # $Source: E:/cvsroot/python_doc/pep8.txt,v $
}}}


    这个行应该包含在模块的文档字符串之后,所有代码之前,上下用一个空行分割.

{{{对于CVS的服务器工作标记更应该在代码段中明确出它的使用
如:在文档的最开始的版权声明后应加入如下版本标记:
# 文件:$id$
# 版本: $Revision$
这样的标记在提交给配置管理服务器后,会自动适配成为相应的字符串,如:
# 文件:$Id: ussp.py,v 1.22 2004/07/21 04:47:41 hd Exp $
# 版本: $Revision: 1.4 $
----HD
}}}


== 命名约定 ==
(Naming Conventions)


    Python库的命名约定有点混乱,所以我们将永远不能使之变得完全一致---
    不过还是有公认的命名规范的.
    新的模块和包(包括第三方的框架)必须符合这些标准,但对已有的库存在不同风格的,
    保持内部的一致性是首选的.

=== 描述:命名风格 ===
(Descriptive: Naming Styles)


    有许多不同的命名风格.以下的有助于辨认正在使用的命名风格,独立于它们的作用.

    以下的命名风格是众所周知的:

    * b (单个小写字母)
    * B (单个大写字母)
    * 小写串 如:getname
    * 带下划的小写串 如:_getname
    * 大写串 如:GETNAME
    * 带下划的大写串 如:_GETNAME
    * `CapitalizedWords`(首字母大写单词串) (或 `CapWords`, `CamelCase` --
    这样命名是由于它的字母错落有致的样子而来的.
     这有时也被当作`StudlyCaps`. 如:`GetName`
    * `mixedCase` (混合大小写串)(与首字母大写串不同之处在于第一个字符是小写如:getName)
    * `Capitalized_Words_With_Underscores`(带下划线的首字母大写串) (丑陋!)



    * 还有一种使用特别前缀的风格,用于将相关的名字分成组.这在Python中不常用,
    * 但是出于完整性要提一下.例如,{{{
os.stat ()函数返回一个tuple,
 他的元素传统上有象st_mode, st_size, st_mtime等等这样的名字.

X11库的所有公开函数以X开头.
}}}
(在Python中,这个风格通常认为是不必要的,    因为属性和方法名以对象作前缀,而函数名以模块名作前缀.)



    另外,以下用下划线作前导或结尾的特殊形式是被公认的(这些通常可以和任何习惯组合(使用?)):



    * _single_leading_underscore(以一个下划线作前导): 弱的"内部使用(internal use)"标志.
     * (例如,"from M import *"不会导入以下划线开头的对象).

    * single_trailing_underscore_(以一个下划线结尾): 用于避免与Python关键词的冲突,例如.
     * "Tkinter.Toplevel(master, class_='ClassName')".

    * {{{__double_leading_underscore}}}(双下划线): 从Python 1.4起为类私有名.


    * {{{__double_leading_and_trailing_underscore__}}}: 特殊的(magic)
    对象或属性,存在于用户控制的(user-controlled)名字空间,
    例如:{{{__init__}}}, {{{__import__}}} 或 {{{__file__}}}. 有时它们被用户定义,
    用于触发某个特殊行为(magic behavior)(例如:运算符重载);
    有时被构造器(infrastructure)插入,以便自己使用或为了调试.
    因此,在未来的版本中,构造器(松散得定义为Python解释器和标准库)
    可能打算建立自己的魔法属性列表,用户代码通常应该限制将这种约定作为己用.
    欲成为构造器的一部分的用户代码可以在下滑线中结合使用短前缀,例如.
    {{{__bobo_magic_attr__}}}.

=== 说明:命名约定 ===
(Prescriptive: Naming Conventions)
==== 应避免的名字 ====
(Names to Avoid)

      永远不要用字符`l'(小写字母el(就是读音,下同)),
      `O'(大写字母oh),或`I'(大写字母eye)作为单字符的变量名.
      在某些字体中,这些字符不能与数字1和0分开.当想要使用'l'时,用'L'代替它.

==== 模块名 ====
(Module Names)

      模块应该是不含下划线的,简短的,小写的名字.


      因为模块名被映射到文件名, 有些文件系统大小写不敏感并且截短长名字,
      模块名被选为相当短是重要的---这在Unix上不是问题, 但当代码传到Mac
      或Windows上就可能是个问题了.


      当一个用C或C++写的扩展模块有一个伴随的Python模块,这个Python模块提供了
       一个更高层(例如,更面向对象)的接口时,C/C++模块有一个前导下划线(如:_socket)


      Python包应该是不含下划线的,简短的,全小写的名字.

==== 类名 ====
(Class Names)

      几乎没有例外,类名总是使用首字母大写单词串(`CapWords`)的约定.

==== 异常名 ====
(Exception Names)

      如果模块对所有情况定义了单个异常,它通常被叫做"error"或"Error".
      似乎内建(扩展)的模块使用"error"(例如:os.error),
      而Python模块通常用"Error" (例如: xdrlib.Error).
      趋势似乎是倾向使用`CapWords`异常名.

==== 全局变量名 ====
(Global Variable Names)

      (让我们希望这些变量打算只被用于模块内部)
      这些约定与那些用于函数的约定差不多.被设计可以通过"from M import *"来使用的
       那些模块,应该在那些不想被导入的全局变量(还有内部函数和类)前加一个下划线).

==== 函数名 ====
(Function Names)

      函数名应该为小写,可能用下划线风格单词以增加可读性.
      `mixedCase`仅被允许用于这种风格已经占优势的上下文(如: threading.py)
      以便保持向后兼容.

==== 方法名和实例变量 ====
(Method Names and Instance Variables)


      这段大体上和函数相同:通常使用小写单词,必要时用下划线分隔增加可读性.



      使用一个前导下划线仅用于不打算作为类的公共接口的内部方法和实例变量.
      Python不强制要求这样; 它取决于程序员是否遵守这个约定.



      使用两个前导下划线以表示类私有的名字. Python将这些名字和类名连接在一起:
      如果类Foo有一个属性名为 {{{__a}}}, 它不能以{{{Foo.__a}}}访问.
      (执著的用户(An insistent user)还是可以通过{{{Foo._Foo__a}}}得到访问权.)
      通常,双前导下划线应该只用来避免与类(为可以子类化所设计)中的属性发生名字冲突.

==== 继承的设计 ====
(Designing for inheritance)

      始终要确定一个类中的方法和实例变量是否要被公开.
      通常,永远不要将数据变量公开,除非你实现的本质上只是记录.
      人们总是更喜欢给类提供一个函数的接口作为替换
      (Python 2.2 的一些开发者在这点上做得非常漂亮).



      同样, 确定你的属性是否应为私有的.私有与非公有的区别在于:
      前者永远不会被用在一个派生类中,而后者可能会.
      是的,你应该在大脑中就用继承设计好了你的类.


      私有属性必须有两个前导下划线,无后置下划线.


      非公有属性必须有一个前导下划线,无后置下划线.



      公共属性没有前导和后置下划线,除非它们与保留字冲突,
      在此情况下,单个后置下划线比前置或混乱的拼写要好,
      例如:class_优于klass.
      最后一点有些争议; 如果相比class_你更喜欢klass,那么这只是一致性问题.


== 设计建议 ==
(Programming Recommendations)


    * 同象None之类的单值进行比较,应该永远用:'is'或'is not'来做.
    当你本意是"if x is not None"时,对写成"if x"要小心 --
    例如当你测试一个默认为None的变量或参数是否被设置为其它值时.
    这个其它值可能是一个在布尔上下文中为假的值!



    * 基于类的异常总是好过基于字符串的异常.
    模块和包应该定义它们自己的域内特定的基异常类(base exception class),
    基类应该是内建的Exception类的子类.
    还始终包含一个类的文档字符串.例如:
{{{
#!python
        class MessageError(Exception):
            """Base class for errors in the email package."""
}}}



    * 使用字符串方法(methods)代替字符串模块,除非必须向后兼容Python 2.0以前的版本.
    字符串方法总是非常快,而且和unicode字符串共用同样的API(应用程序接口)



    * 在检查前缀或后缀时避免对字符串进行切片.

    用startswith()和endswith()代替, 因为它们是明确的并且错误更少.
    例如:
{{{
        No:  if foo[:3] == 'bar':
        Yes: if foo.startswith('bar'):
}}}


      例外是如果你的代码必须工作在Python 1.5.2 (但是我们希望它不会发生!).
    * 对象类型的比较应该始终用isinstance()代替直接比较类型.例如:
{{{
        No:  if type(obj) is type(1):
        Yes: if isinstance(obj, int):
}}}

      检查一个对象是否是字符串时,紧记它也可能是unicode字符串!
      在Python 2.3, str和unicode有公共的基类,basestring,所以你可以这样做:
{{{
#!python
        if isinstance(obj, basestring):
}}}

      在Python 2.2 类型模块为此定义了StringTypes类型, 例如:
{{{
#!python
        from types import StringTypes
        if isinstance(obj, StringTypes):
}}}


      在Python 2.0和2.1,你应该这样做:
{{{
#!python
        from types import StringType, UnicodeType
        if isinstance(obj, StringType) or \
           isinstance(obj, UnicodeType) :
}}}


    * 对序列,(字符串(strings),列表(lists),元组(tuples)),
    使用空列表是false这个事实,因此"if not seq"或"if seq"比
    "if len(seq)"或"if not len(seq)"好.



    * 书写字符串文字时不要依赖于有意义的后置空格.
    这种后置空格在视觉上是不可辨别的,并且有些编辑器(特别是近来,reindent.py)
    会将它们修整掉.


    * 不要用 == 来比较布尔型的值以确定是True或False(布尔型是Pythn 2.3中新增的)
{{{
        No:  if greeting == True:
        Yes: if greeting:

        No:  if greeting == True:
        Yes: if greeting:
}}}


测试pdf附件


====正文开始====
这是邮件正文
====正文结束====

test how blogger will deal with attachment

this is in the attachment, and the file format is txt.This is in the mail body.

上海书展记事

时间:周六(2007--8-18)下午两点
天气:艳阳高照(热啊)
交通:陪了我近三年的凤凰自行车
地点:愚园路1032弄--兴义路99号(上海世贸商城)
事件:参观上海书展
结果:不爽
理由有二:
      其一:被收取十元人民币的门票费,没有听过去买书还要收门票的
      其二:再次被中国人口之多所震惊,虽然已早有准备

星期六, 八月 18, 2007

Perl 编程格式指南

名称

P5EEx::Blue::perlstyle - P5EE 样式指导 P5EE 是 Perl 5 Enterprise Environment (企业型 Perl5 样式指导)的缩写。

介绍

在P5EE发行版中,包含的所有代码和文件都遵循了本文所述的样式。请注意:这些样式并不是要抑制你的创造力,而是想要使那些阅读到你代码的家伙们的生活变得更容易一些。他也可以用来解决分歧,避免个人间纠纷。 下面的这些约定适用于perl模块,web程序(CGI/mod_perl)以及命令行程序。当然,这些规则也在一定程度上适用于用P5EE写的perl代码。 注意,这些都是指导性的方针,不是必须遵守的规则。如果在这里你真的需要违反其中一条的话,无论如何,最好先问问P5EE核心团队。 另外,本文档中的大部分,并不是强调正确的方法就是我们的方法。我们需要有一些约定来让每个人的生活更容易一些。 如果你有什么问题,可以在P5EE开发邮件列表中询问,p5ee@perl.org. http://lists.perl.org/showlist.cgi?name=p5ee P5EE项目所需文件在下面这些网站上可以找到。 http://p5ee.perl.org/ http://www.officevision.com/pub/p5ee 本文档将随时间不断更新,每份最新的文档结尾,都会有历次版本的变动记录。

编码规则

Perl版本

我们是在perl5.005_03上编写所有代码的。也许有一天我们应该利用以下perl5.6的特性。不管怎样,所有代码应该在 perl5.005_03以及后续版本上都能运行。P5EE所有的核心代码都在perl5.005_03和perl5.6.0上测试过,虽然现在P5EE 用在perl5.6上比较多。

文档

所有的模块文档都应参照模块模板文件中的POD例子来撰写,解释模块的方法,目的,用途和每个带有名字,描述,输入,输出,受影 响因素等信息的公用API。 文档中,如果需要返回一个数组或哈希表的引用,要记录数组的大小(包括恰当地描述每个元素都是什么)和哈希表中每个键的名字。如果是复合数据结构的话,尽 量描绘出合适的结构。 另外,在文档中还要记录出返回数据是什么类型的数值。是整型,还是一段HTML代码,还是一个布尔值? 所有命令行程序的选项都要使用命令行程序样本文件中的代码来记录。每个有效的方法,switch结构等等都要被记录,连同方法的描述,目的,程序如何使 用。不要尝试对不同目的的程序使用相同的选项。 对于所有的WEB程序,应当在程序注释段中说明功能,目的和使用。 任何外部文档,命令行程序和模块的文档都要写成POD样式。这样的话,我们就可以把它们通过多种pod2xxx的转换器转换成各种格式的其他文档。POD 不是一种正规的标签语言,它只是一种可以让文档非常容易转换成其他格式的方法。如果你有什么问题的话,可以自己看一下Perl自带的 perlpod帮助页 或者 询问我们及其其他懂POD的人。Perl中国推广组的FPC里面,也有一篇 [[fayland]] 所写的关于 [[如何撰写POD]] 的文章。

版本

分别使用模块,web程序,命令行程序样本文件中版本的代码。模块中的$VERSION将会反映出CVS的版本。Makefile.PL文件应该包含版本描述,并且还应该不依赖于CVS仓库中任何一个单独文件的版本。 同样,拥有$VERSION的XS模块也会反映出发行版的版本,否则每当你对文件做了一点改动,你都需要重新编译那些已经共享的库。这对于从事开发工作中的人来讲,的确是一种痛苦。 我们的发行版本号使用tuples模式,第一个数字是主修订号,第二个数字是版本号,第三个数字是子版本号。Odd-numbered版本号是开发版本。例如:
1.0.0       P5EE 1 的首发版
1.0.1 P5EE 1.0的第二次发布版
1.0.10 P5EE 1.0的第二次发布版的第一次修正版
1.1.0 P5EE 1.1的首发版
2.0.0 P5EE 2 的首发版
版本号后面也可以跟一个连字符和一些文字,来表示特别的版本,或者给出额外的信息。例如 1.1.4-bender 注意,这是一个狂欢版 1.5.0-pre1 注意这不是最后的版本,而是预览版。 在perl5.6.0中,你可以使用象v2.0.0这样的版本,但是在前几版的perl是不允许的。所以要把一个tuple版本号转换成一个用$VERSION表示的版本号字符串,使用正常的整数来表示主修订号,三个数字来做版本号和三个数字来做子版本号。例如:

1.1.6 -> 1.001006
2.0.0 -> 2.000000
这样,perl就可以通过大于和小于比较两个版本字符串。 LinuxForum中有一篇关于 软件发行惯例 的中文译版:http://www.linuxforum.net/books/srp/Software-Release-Practice-HOWTO.html

注释

所有的代码都应该尽可能地具有可读性。所以,我们的代码中可以只包含一些对于不明朗内容的必要注释。于是,我们应该使用象'$story_count'这样的名字,而不是这么做:

# story count
my $sc = 0;
也许要别人理解代码需要包含一些整齐的注释。有时一个简单的单行注释就可以解释后面代码的用途。另外,我们有时还需要对一个复 杂的算法需要每行都要注释。Kernighan 和 Pike 写的《Practice of Programming》中关于注释的部分值得一看。

警告和严格语法

所有代码都必须使用'use strict'和打开perl的-w选项来编译和运行。当然,如果你必须禁止 -w 或 strict 的使用,我们也是没有意见的。(实际上你真正需要这么做的机会将会很渺茫)。 有一个例外是"使用了未初始化的变量"警告,我们在P5EE.pm中禁掉了它,所以在你的代码中如何包含了"use P5EE",那么你就不必担心这些警告了。

词汇范畴变量

只使用词汇范畴变量,除了一些特殊的全局变量($VERSION,%ENV,@ISA,$!等等)和一些极特殊的情况。把 全局变量当成普通变量从来都是不恰当的,如有必要,使用"use vars"来声明全局变量,而不是使用our()函数(our()在perl5.6中有介绍)。 词汇范畴内的变量使用my()来创建。一个全局变量是预先存在的(如果他是一个特殊变量),或者当他被使用的时候就刚刚创建。local()被用来告诉 perl为一个变量赋一个临时值。这应该只用于象$/这样的特殊变量,或者在特殊的环境中。如果你必须给某个全局变量赋值的话,那么你最好先考虑考虑是否 需要用local()。 local()也可以用于数组或者哈希表中的元素,尽管这很不常用。

输出

默认情况下不要从模块中输入任何东西。你可以自由地在@EXPORT_OK中放入任何你想放的东西,这样你的模块的使用者就可以明确地请求这些符号。(例如:"use P5EE::Something qw(getFoo setFoo)"),但是不要默认地输出它们。

传递引用参数

方法要获取或者返回数组和哈希表的时候最好只使用其引用。注意列表和数组不是一回事。下面这样就很好:

return($user, $form, $constants);
当数组不确定的时候也许是一个例外。

my @return = ($user, $form);
push @return, $constants if $flag;
return @return;
尽管如此,但是,为了提高代码的效率和可读性,我们更趋向于编写这样的代码:

if ($flag) {
return($user, $form, $constants);
} else {
return($user, $form);
}

垃圾收集

perl在垃圾收集方面的确作的很好。它会自动清理那些过了生存期的词汇范畴变量和已经没有引用计数的对象。如果你使用词汇范畴的话,通常你不必担心这个。 然而,有一些粘合的代码,比如用C编译的代码连接到perl,perl也许就不会自动帮你清理了。在这种情况下,你就得自己动手了。如果在那些粘合的代码中有实现此功能的方法,那么使用这些方法是比较适合的。 还有,如果你有一个运行时间会很长的函数,里面有一个巨大的数据结构。那么希望你尽可能地在它运行完之后立刻释放一下内存。

my $huge_data_structure = get_huge_data_structure();
do_something_with($huge_data_structure);
undef $huge_data_structure;

__END__ 和 __DATA__ 和 __PACKAGE__

在web程序中不要使用__END__或者__DATA__。它们会与mod_perl冲突。还有,在web程序中__PACKAGE__也许不会返回你期望的值。但它们在模块中运行良好。

测试

模块应该提供测试代码,还有如何使用它们的文档。

STDIN/STDOUT

永远使用P5EE的日志工具来报错。不要直接打印到STDERR上。也不要直接打印到STDOUT上。除非你需要直接打印到用户的浏览器上。 在命令行程序中,根据你的需要打印到STDERR或STDOUT上都可以。

文件和Globs

为了建立和分析文件路径,请使用Filel::Spect::Functions 和 File::Basename 模块。要创建和销毁路径,使用File::Path模块。这在非Unix平台中比较方便。

my $path = "$dir/$file"; # 错
my $path = catfile($dir, $file); # 对

my $dir = "."; # 错
my $dir = curdir(); # 对

mkdir("/path"), mkdir("/path/to"), ... # 错
`mkdir /path`; `mkdir /path/to`, ... # 大错特错
mkpath("/path/to/my/dir", 0, 0775); # 对
请使用 opendir() readdir() 来代替 glob 操作符(glob('*') 或者 <*>)。注意 glob() 在perl5.6中比以往版本更轻便了,但它还是不十分可靠,每个perl的安装都可以选择通过 File::Glob 模块来用本地的习惯来代替这个默认的。 不要为了任何事而使用向*foo这样的符号表 globs(和上面提到的 glob 不一样),除非有必要必须直接操作符号表。其实这从来都是不必要的。

系统调用

永远都要在系统调用后检查返回值。包括 open(),close(),mkdir() 或者其他直接面对系统的对话。perl内置的系统调用都会把错误信息返回到$!;模块中的一些方法可能会将错误返回到$@或者其他地方,如果你不知道的话 就去查模块的文档。永远要这么做,哪怕只是调用一个 errorLog(),当返回的值不是你期望那样的时候。

样式

很多样式的描述都是取自perl样式man文档。我们在这里做了一些改动,但读一读那些文档也是个不错的主意。

术语

P5EE项目的名字叫做"P5EE",并没有"P5EE1"或者"P5EE2",要说明版本的话,使用"P5EE 2.0"或者"P5EE2.0.1"。 函数'与'子程序'与'方法'的区别: *'方法'应该只用于指向对象方法或者类的方法;也就是那些使用面向对象编程的,第一个参数是对象或者是类的函数。通常意义上的*'子程序',它们不是对象或者类的方法,而是函数。类中创建和返回对象的方法称为构造器。

名字

不要使用一个字符的变量,除非是迭代中的指示变量。 也不要使用两个字符的变量,这和使用一个字符的变量没区别。 常量全部都大写;这些是在整个程序中都不会变化的变量。

$Minimum = 10; # 错
$MAXIMUM = 50; # 对
其他变量小写,用下划线来连接单词。这些单词通常都用名词(一般都是单数名词),除非变量被用来指示某些动作的标记,那么这些变量的名字可以使用描述该动作的动词(或者是动名词)。

$thisVar = 'foo'; # 错
$this_var = 'foo'; # 对
$work_hard = 1; # 对,动词,布尔类型的标记
$running_fast = 0; # 对,动名词,布尔类型的标记
数组和哈希应该是复数的名词,不管是正常的数组和哈希还是数组引用和哈希引用。不要给引用命名为ref或者将其类型加入到名字当中。

@stories = (1, 2, 3); # 对
$comment_ref = [4, 5, 6]; # 错
$comments = [4, 5, 6]; # 对
$comment = $comments->[0]; # 对
让名字易理解。不要使用象$sc这样的名字,你应该用"$story_count"。 方法和函数(除了那些特殊情况,比如AUTOLOAD)应该由动词开头,然后后面跟着完成动作的词汇。词组组成的名字应该全部小写,并用下划线来连接单 词,为了跟'perl样式'指导还有那些CPAN上大部分模块保持一致。这些方法应该尽可能地描绘出它要执行什么,和它要返回什么数据。

$obj->getStory(); # 错
$obj->setStoryByName(); # 又错了
$obj->getStoryByID(); # 还是错!这不是Java!

$obj->get_story(); # 对
$obj->set_story_by_name(); # 对
$obj->get_story_by_id(); # 对
以下划线开头的方法和函数是特殊的:它们在当前文件之外是无法使用的。(也就是'private'--私有的)。但这不是代码本身强制要求的,而只是编程人员的习惯而已。 对于大的for循环结构,不要使用$_,而是用具名的变量。不要用$_(或者假想它)除非你非常明确是怎么回事,或者需要它的时候(比如在map()和grep()中)。

for (@list) {
print; # 对;每个人都知道这是什么意思
print uc; # 不好;很少有人知道这个
print uc $_; # 看起来好一些了
}
注意,如果可能的话,请使用特殊变量'_'。它是一个占位符,可以传递给stat()和文件测试符,它让重新判断文件的状态变 得简单一些。在下面这个例子中。在每个文件测试中都要使用变量$file。使用'_'来代替是个不错的选择。尽管最后一次测试的文件和你想像的一样,你还 是应该小心一些。

if (-d $file) { # $file现在是一个目录
# ...
} elsif (-l _) { # $file现在是一个symlink # ...
}
包的名字应该每个单词的第一个字母大写,其余的小写。
    P5EE::Standard          #  好
P5EE::Authz #  好
P5EE::MainCode #  好
在POD文档中全部使用小写。
    P5EE::styleguide        # 对于文档来说还不错
为模块命名应该遵循下面几条规则。 所有被p5ee@perl.org邮件列表广泛支持的P5EE的服务都应该进入P5EE的包中 '命名样式'应该和CPAN上的模块类似。 '选择命名'借鉴CPAN上其他模块的先例。 '选择命名'借鉴J2EE的先例。 那些还没有确定要作为对象被初始化的包应该用一个'形容词'或者'概念'来作名字(也就是 P5EE::Standard) 那些已经确定要作为对象被促使化的模块和类包应该用伴有隐含变化形容词含义的名词来作名字(也就是说 P5EE::Authen::Principal)。

缩进

代码检验后进入CVS必须不能包含制表符。带有制表符的代码片断不能很好的通过email传递,而且不同的人对制表符拦截有不同的设置。如果你想为你的编辑器设置制表符拦截,只要保证它保存文件的时候,把制表符转换为空格就可以了。 一般块样式代码应该缩进4个空格。Emacs和vim的设置如下。

* x?emacs: cperl-mode

.xemacs/custom.el:
------------------
(custom-set-variables
'(cperl-indent-level 4)
'(cperl-continued-statement-offset 4)
'(cperl-tab-always-indent t)
'(indent-tabs-mode nil)
)

* vim

.vimrc:
-------
set expandtab " replaces any tab keypress with the appropriate number of spaces
set tabstop=4 " sets tabs to 4 spaces
" 将制表符换成适当数量的空格
" 将制表符换成4个空格

行的长度

行的最长长度应该是77列(对未折行的应该有75列),这是为了最大限度地适用于不同人的开发环境和为了更好的用email来 传输而适应不同的email客户端。(也为了补丁)。 例如:Eudora3.0.6 会在第76个字符处,使用一行实心线来限制第80个非空白字符的输出。如果行中间有空格,则它允许在最后一个词后自动换行到下一个具有78个字符的行中。 如果源文本没有一行超过77个字符的情况,那么"diff -u"命令则会添加一列,当然这列是不会被折行的。

空白

在结束句子的分号之前不要留空白。

foo(@bar) ; # 错
foo(@bar); # 对
垂直对齐。

my $foo = 1;
my $bar = 2;
my $xyzzy = 3;

open(FILE, $fh) or die $!;
open(FILE2, $fh2) or die $!;

$rot13 =~ tr[abcedfghijklmnopqrstuvwxyz]
[nopqrstuvwxyzabcdefghijklm];

# note we use a-mn-z instead of a-z,
# for readability
$rot13 =~ tr[a-mn-z]
[n-za-m];
为了增加可读性,下列操作时需要一些空白: * 在做不同工作的代码块间,添加空行。 * 在变量声明完成之后,添加空行。 * 在最终的return()代码之前,添加空行。 * 在块的前面和后面添加空行,如果前面是注释的话,则不用添加空行。 一个例子:

# this is my function! 这是我的函数
sub foo {
my (@data) = @_;
my $obj = new Constructor;
my ($var1, $var2);

$obj->setFoo($data[1]);
$var1 = $obj->getFoo(1);
$var2 = $obj->getFoo($var1);

display($var1, $var2);

return($data[0]);
}

print 1;

圆括号

在流程结构中,在关键词和圆括号对之间要有空格。但对于函数,则不必。

for(@list) # 错
for (@list) # 对

my ($ref) # 对
my($ref) # 首选

localtime ($time); # 错
localtime($time); # 对
对带圆括号的列表和标量上下文要仔细!

my @array = ('a', 'b', 'c');
my ($first_element) = @array; # a
my ($first_element) = ('a', 'b', 'c'); # a
my $element_count = @array; # 3
my $last_element = ('a', 'b', 'c'); # c
永远在函数后面加上圆括号,尽管没有参数也是如此。但有一些例外,比如列表操作符(比如print)和一元操作符(像undef,delete,uc等等)。 如果为了可读性,可以在圆括号中添加空格,否则不要添加。

for ( map { [ $_, 1 ] } @list ) # 可以
for ( @list ) # 也还可以。
在多行的表达式中,用开始的语句或前圆括号来对齐后圆括号都可以。

@list = qw(
bar
baz
); # 正确

if ($foo && $bar && $baz
&& $buz && $xyzzy
) {
print $foo;
}
在后圆括号之后是否添加空格取决于它后面要跟什么。

print foo(@bar), baz(@buz) if $xyzzy;
还要注意,在允许省略圆括号的情况下-单行控制流程表达式,比如if $xyzzy,对于程序员来说,如果你十分明确它的含义的话,你可以省略圆括号。其实在上面的这种情况在$xyzzy两边是完全可以省略掉圆括号的。所以 就省略掉它们,以增加可读性。如果有问题的话,那最好就别省略圆括号了。 同样原则也适用于perl的内置函数,当你很确定的情况下(例如,在语句中只有一句调用函数的语句,或者函数调用被流控制操作符分割)。用户自定义函数必 须加上圆括弧。

print 1, 2, 3; # 好的
delete $hash{key} if isAnon($uid); # 好的
不管怎样,如果有什么疑惑的情况,最好加上圆括弧,记住perl样式man文档中Larry Wall的一句话: 当你有疑惑的时候,加上圆括号,至少,它可以让一些可怜的笨家伙在vi中能用%跳舞。(在vi中%用来寻找匹配的括号) 尽管你不处于疑惑之中,将来使用你的代码的人,就算是为他们的大脑神经做做公益事业,你还是加上圆括号吧,省得将来他们自己加括号也许会加错地方。 所以,当程序员非常清楚的情况下,省略圆括号,但是如果有任何问题的话,就不要省略它们。

花括号

(这是关于流程控制中的花括号,不是哈希等数据结构中的花括号。) 在前花括号之前一定有一个空格

while (<$fh>){ # 错
while (<$fh>) { # 对
一行的代码块可以写在一行中。那代码后面的分号可以省略。
    for (@list) { print }
否则,在每一行代码结束的地方添加分号,在第一行写关键字和前花括号,后花括号和后面的关键字写在一行。
    for (@list) {
print;
smell();
}
perl样式中常用"单臂elses":(译者注:就是else旁边只有一个单花括号。)

# 对
if ($foo) {
print;
}
else {
die;
}


# 错
if ($foo) {
print;
} else {
die;
}

操作符

多数操作符两边可以添加空格。主要的例外是从美学角度出发的;例如,"**"符号两边的空格是省略的。另外,在","的前面没有空格,而在后面往往有一个空格。
   print $x , $y;   # 错
print $x, $y; # 对

$x = 2 >> 1; # 好
$y = 2**2; # 好
注意"&&"和"||"的优先级高于"and"和"or"。除此之外完全相同。建议使用低优先级的来控制流程,高优先级的用来测试/返回值。例如:
    $bool = $flag1 or $flag2;       # 错误!无法运行
$value = $foo || $bar; # 正确
open(FILE, $file) or die $!;

$true = foo($bar) && baz($buz);
foo($bar) and baz($buz);
注意上面"and"很少使用and,因为上面的语句比用"if"写要好一些。
    baz($buz) if foo($bar);
大多数情况下,and和&&,or和||之间的混淆可以通过使用圆括号很好地解决。如果你不使用圆括号,那么你必须正确使用 操作符。但如果你使用了圆括号--通常,如果有问题的话,应该使用--那么不管你用什么操作符都关系不大。从易读角度来看也好,从美学角度来看也好,一定 要在你的代码块中保持风格一致。 在很长的代码行中在操作符之后断行,但"and","or","&&","||"例外。对于二元的操作符来讲,符号两边的数据要尽量保持 整齐。
    print "foo" . "bar" . "baz"
. "buz"; # 错

print "foo" . "bar" . "baz" .
"buz"; # 对

print $foo unless $x == 3 && $y ==
4 && $z == 5; # 错

print $foo unless $x == 3 && $y == 4
&& $z == 5; # 对

其他

在括号或者花括号中间有复合下标索引时,在两边加上空格,
    $foo{$bar{baz}{buz}};       # 不错
$foo{ $bar{baz}{buz} }; # 更好
总的来说,单引号之间表示是纯字符,双引号之间表示是允许内插的文本。 在花括号之间的名称和当使用=>符号的情况下可以省略引号,但是一定要小心这个名称不要和函数名重复。如果有重复,则必须使用引号。
    $what{'time'}{it}{is} = time();
当创造复合语句的时候,把主要的执行动作放在前面。
    open(FILE, $fh) or die $!;      # 对
die $! unless open(FILE, $fh); # 错

print "Starting\n" if $verbose; # 对
$verbose && print "Starting\n"; # 错
使用"打印至"来代替重复使用print语句。
        print <
只要记住,除非你在"打印至"的记号两边写上单引号(<<'EOT'),那么其间的文本是可以内插的。所以要打印"$"和"@"符号是需要转义的。

致谢

这篇样式指导是以slashcode样式指导为基础。 它也和mod_perl样式指导保持一致,它是C语言阿帕奇样式指导中的精华。 http://slashcode.com/docs/slashstyle.html http://cvs.apache.org/viewcvs.cgi/modperl-docs/src/devel/modperl_style/modperl_style.pod?rev=1.5 http://dev.apache.org/styleguide.html 最新中译版在Perl中国推广组FPC中会随时更新。

更新

$Log: perlstyle.pod,v $ Revision 1.2 2001/11/30 16:00:52 spadkins Renamed 'Component' to 'Service' throughout. Improved perldocs. Revision 1.1 2001/11/22 05:16:59 spadkins Major new architectural framework proposal Revision 1.1 2001/11/16 23:21:38 spadkins initial stuff

版本

原版 $Id: perlstyle.pod,v 1.2 2001/11/30 16:00:52 spadkins Exp $ 中译版 $Id: perlstyle.pod,v 1.2 2005/05/02 13:00:52 spadkins Exp $