2008年8月31日

正则表达式的五个成功习惯

正则表达式难于书写、难于阅读、难于维护,经常错误匹配意料不到的文本或者错过了有效的文本,这些问题都是由正则表达式的表现和能力引起的。每个元字符(metacharacter)的能力和细微差别组合在一起,使得代码不借助于智力技巧就无法解释。
     许多包含一定特性的工具使阅读和编写正则表达式变得容易了,但是它们又很不符合习惯。对于很多程序员来说,书写正则表达式就是一种魔法艺术。他们坚持自己所知道的特征并持有绝对乐观的态度。如果你愿意采用本文所探讨的五个习惯,你将可以让你设计的正则表达式经受的住反复试验。
    本文将使用Perl、PHP和Python语言作为代码示例,但是本文的建议几乎适用于任何替换表达式(regex)的执行。

    一、使用空格和注释
    对于大部分程序员来说,在一个正则表达式环境里使用空格和缩进排列都不成问题,如果他们没有这么做一定会被同行甚至外行人士看笑话。几乎每个人都知道把代码挤在一行会难于阅读、书写和维护。对于正则表达式又有什么不同呢?
    大部分替换表达式工具都具有扩展的空格特性,这允许程序员把他们的正则表达式扩展为多行,并在每一行结尾加上注释。为什么只有少部分程序员利用这个特性呢?Perl 6的正则表达式默认就是扩展空格的模式。不要再让语言替你默认扩展空格了,自己主动利用吧。
    记住扩展空格的窍门之一就是让正则表达式引擎忽略扩展空格。这样如果你需要匹配空格,你就不得不明确说明。
    在Perl语言里面,在正则表达式的结尾加上x,这样“m/foo|bar/”变为如下形式:
m/
  foo
  |
  bar
/x
    在PHP语言里面,在正则表达式的结尾加上x,这样“"/foo|bar/"”变为如下形式:
"/
  foo
  |
  bar
/x"
    在Python语言里面,传递模式修饰参数“re.VERBOSE”得到编译函数如下:
pattern = r'''
foo
|
bar
'''
regex = re.compile(pattern, re.VERBOSE)
    处理更加复杂的正则表达式时,空格和注释就更能体现出其重要性。假设下面的正则表达式用于匹配美国的电话号码:
\(?\d{3}\)? ?\d{3}[-.]\d{4}
     这个正则表达式匹配电话号码如“(314)555-4000”的形式,你认为这个正则表达式是否匹配“314-555-4000”或者“555- 4000”呢?答案是两种都不匹配。写上这么一行代码隐蔽了缺点和设计结果本身,电话区号是需要的,但是正则表达式在区号和前缀之间缺少一个分隔符号的说明。
    把这一行代码分成几行并加上注释将把缺点暴露无疑,修改起来显然更容易一些。
    在Perl语言里面应该是如下形式:
/  
    \(?     # 可选圆括号
      \d{3} # 必须的电话区号
    \)?     # 可选圆括号
    [-\s.]? # 分隔符号可以是破折号、空格或者句点
      \d{3} # 三位数前缀
    [-.]    # 另一个分隔符号
      \d{4} # 四位数电话号码
/x
    改写过的正则表达式现在在电话区号后有一个可选择的分隔符号,这样它应该是匹配“314-555-4000”的,然而电话区号还是必须的。另一个程序员如果需要把电话区号变为可选项则可以迅速看出它现在不是可选的,一个小小的改动就可以解决这个问题。

    二、书写测试
    一共有三个层次的测试,每一层为你的代码加上一层可靠性。首先,你需要认真想想你需要匹配什么代码以及你是否能够处理错误匹配。其次,你需要利用数据实例来测试正则表达式。最后,你需要正式通过一个测试小组的测试。
     决定匹配什么其实就是在匹配错误结果和错过正确结果之间寻求一个平衡点。如果你的正则表达式过于严格,它将会错过一些正确匹配;如果它过于宽松,它将会产生一个错误匹配。一旦某个正则表达式发放到实际代码当中,你可能不会两者都注意到。考虑一下上面电话号码的例子,它将会匹配“800-555-4000  = -5355”。错误的匹配其实很难发现,所以提前规划做好测试是很重要的。
    还是使用电话号码的例子,如果你在Web表单里面确认一个电话号码,你可能只要满足于任何格式的十位数字。但是,如果你想从大量文本里面分离电话号码,你可能需要很认证的排除不符合要求的错误匹配。
    在考虑你想匹配的数据的时候,写下一些案例情况。针对案例情况写下一些代码来测试你的正则表达式。任何复杂的正则表达式都最好写个小程序测试一下,可以采用下面的具体形式。
    在Perl语言里面:
#!/usr/bin/perl

my @tests = ( "314-555-4000",
              "800-555-4400",
       "(314)555-4000",
              "314.555.4000",
              "555-4000",
              "aasdklfjklas",
              "1234-123-12345"          
            );

foreach my $test (@tests) {
    if ( $test =~ m/
                   \(?     # 可选圆括号
                     \d{3} # 必须的电话区号
                   \)?     # 可选圆括号
                   [-\s.]? # 分隔符号可以是破折号、空格或者句点
                     \d{3} # 三位数前缀
                   [-\s.]  # 另一个分隔符号
                     \d{4} # 四位数电话号码
                   /x ) {
        print "Matched on $test\n";
     }
     else {
        print "Failed match on $test\n";
     }
}

    在PHP语言里面:
<?php
$tests = array( "314-555-4000",
           "800-555-4400",
           "(314)555-4000",
           "314.555.4000",
           "555-4000",
           "aasdklfjklas",
           "1234-123-12345"
          );

$regex = "/
            \(?     # 可选圆括号
              \d{3} # 必须的电话区号
            \)?     # 可选圆括号
            [-\s.]? # 分隔符号可以是破折号、空格或者句点
              \d{3} # 三位数前缀
            [-\s.]  # 另一个分隔符号
              \d{4} # 四位数电话号码
           /x";

foreach ($tests as $test) {
    if (preg_match($regex, $test)) { 
        echo "Matched on $test<br />;";
    }
    else {
        echo "Failed match on $test<br />;";
     }
}
?>;

        在Python语言里面:
import re

tests = ["314-555-4000",
         "800-555-4400",
         "(314)555-4000",
         "314.555.4000",
         "555-4000",
         "aasdklfjklas",
         "1234-123-12345"        
        ]

pattern = r'''
\(?                 # 可选圆括号
              \d{3} # 必须的电话区号
            \)?     # 可选圆括号
            [-\s.]? # 分隔符号可以是破折号、空格或者句点
              \d{3} # 三位数前缀
            [-\s.]  # 另一个分隔符号
              \d{4} # 四位数电话号码
           '''

regex = re.compile( pattern, re.VERBOSE )

for test in tests:
    if regex.match(test):
        print "Matched on", test, "\n"
    else:
        print "Failed match on", test, "\n"

    运行测试代码将会发现另一个问题:它匹配“1234-123-12345”。
     理论上,你需要整合整个程序所有的测试到一个测试小组里面。即使你现在还没有测试小组,你的正则表达式测试也会是一个小组的良好基础,现在正是开始创建的好机会。即使现在还不是创建的合适时间,你也应该在每次修改以后运行测试一下正则表达式。这里花费一小段时间将会减少你很多麻烦事。

    三、为交替操作分组
    交替操作符号(|)的优先级很低,这意味着它经常交替超过程序员所设计的那样。比如,从文本里面抽取Email地址的正则表达式可能如下:
^CC:|To:(.*)
    上面的尝试是不正确的,但是这个bug往往不被注意。上面代码的意图是找到“CC:”或者“To:”开始的文本,然后在这一行的后面部分提取Email地址。
     不幸的是,如果某一行中间出现“To:”,那么这个正则表达式将捕获不到任何以“CC:”开始的一行,而是抽取几个随机的文本。坦白的说,正则表达式匹配 “CC:”开始的一行,但是什么都捕获不到;或者匹配任何包含“To:”的一行,但是把这行的剩余文本都捕获了。通常情况下,这个正则表达式会捕获大量 Email地址,所有没有人会注意这个bug。
    如果要符合实际意图,那么你应该加入括号说明清楚,正则表达式如下:
(^CC:)|(To:(.*))
    如果真正意图是捕获以“CC:”或者“To:”开始的文本行的剩余部分,那么正确的正则表达式如下:
^(CC:|To:)(.*)
    这是一个普遍的不完全匹配的bug,如果你养成为交替操作分组的习惯,你就会避免这个错误。

    四、使用宽松数量词
    很多程序员避免使用宽松数量词比如“*?”、“+?”和“??”,即使它们会使这个表达式易于书写和理解。
     宽松数量词可以尽可能少的匹配文本,这样有助于完全匹配的成功。如果你写了“foo(.*?)bar”,那么数量词将在第一次遇到“bar”时就停止匹配,而不是在最后一次。如果你希望从“foo###bar+++bar”中捕获“###”,这一点就很重要。一个严格数量词将捕获“###bar++ +”。
    假设你要从HTML文件里面捕获所有电话号码,你可能会使用我们上文讨论过的电话号码正则表达式的例子。但是,如果你知道所有电话号码都在一个表格的第一列里面,你可以使用宽松数量词写出更简单的正则表达式:
<tr>;<td>;(.+?)<td>;
    很多刚起步的程序员不使用宽松数量词来否定特定种类。他们能写出下面的代码:
<tr>;<td>;([^>;]+)</td>;
    这种情况下它可以正常运行,但是如果你想捕获的文本包含有你分隔的公共字符(这种情况下比如</td>;),这将会带来很大麻烦。如果你使用了宽松数量词,你只要花上很少的时间组装字符种类就能产生新的正则表达式。
    在你知道你要捕获文本的环境结构时,宽松数量词是具有很大价值的。

    五、利用可用分界符
    Perl 和PHP语言常常使用左斜线(/)来标志一个正则表达式的开头和结尾,Python语言使用一组引号来标志开头和结尾。如果在Perl和PHP中坚持使用左斜线,你将要避免表达式中的任何斜线;如果在Python中使用引号,你将要避免使用反斜线(\)。选择不同的分界符或引号可以允许你避免一半的正则表达式。这将使得表达式易于阅读,减少由于忘记避免符号而潜在的bug。
    Perl和PHP语言允许使用任何非数字和空格字符作为分界符。如果你切换到一个新的分界符,在匹配URL或HTML标志(如“http://”或“<br/>;”)时,你就可以避免漏掉左斜线了。
    例如,“/http:\/\/(\S)*/”可以写为“#http://(\S)*#”。
    通用分界符是“#”、“!”和“|”。如果你要使用方括号、尖括号或者花括号,只要保持前后配对出现就可以了。下面就是一些通用分界符的示例:
#…# !…! {…} s|…|…| (Perl only) s[…][…] (Perl only) s<…>;/…/ (Perl only) 
     在Python中,正则表达式首先会被当作一个字符串。如果你使用引号作为分界符,你将漏掉所有反斜线。但是你可以使用“r''”字符串避免这个问题。如果针对“re.VERBOSE”选项使用三个连续单引号,它将允许你包含换行。例如 regex = "(\\w+)(\\d+)"可以写出下面的形式:
regex = r'''
           (\w+)
           (\d+)
         '''

    小结:本文的建议主要着眼于正则表达式的可读性,在开发中养成这些习惯,你将会更加清晰的考虑设计和表达式的结构,这将有助于减少bug和代码的维护,如果你自己就是这个代码的维护者你将倍感轻松。

http://www.phpv.net/html/1506.html

半小时精通正则表达式!

想必很多人都对正则表达式都头疼.今天,我以我的认识,加上网上一些文章,希望用常人都可以理解的表达方式.来和大家分享学习经验.

  开篇,还是得说说 ^  和  $  他们是分别用来匹配字符串的开始和结束,以下分别举例说明

"^The": 开头一定要有"The"字符串;
"of despair$":  结尾一定要有"of despair" 的字符串;

那么,
"^abc$": 就是要求以abc开头和以abc结尾的字符串,实际上是只有abc匹配
"notice": 匹配包含notice的字符串

你可以看见如果你没有用我们提到的两个字符(最后一个例子),就是说 模式(正则表达式) 可以出现在被检验字符串的任何地方,你没有把他锁定到两边
接着,说说 '*', '+',和 '?',
他们用来表示一个字符可以出现的次数或者顺序. 他们分别表示:
"zero or more"相当于{0,},
"one or more"相当于{1,}, 
"zero or one."相当于{0,1},  这里是一些例子:

"ab*":  和ab{0,}同义,匹配以a开头,后面可以接0个或者N个b组成的字符串("a", "ab", "abbb", 等);
"ab+": 和ab{1,}同义,同上条一样,但最少要有一个b存在 ("ab", "abbb", 等.);
"ab?":和ab{0,1}同义,可以没有或者只有一个b;
"a?b+$": 匹配以一个或者0个a再加上一个以上的b结尾的字符串.

要点, '*', '+',和 '?'只管它前面那个字符.

你也可以在大括号里面限制字符出现的个数,比如

"ab{2}": 要求a后面一定要跟两个b(一个也不能少)("abb");
"ab{2,}": 要求a后面一定要有两个或者两个以上b(如"abb", "abbbb", 等.);
"ab{3,5}": 要求a后面可以有2-5个b("abbb", "abbbb", or "abbbbb").

现在我们把一定几个字符放到小括号里,比如:
"a(bc)*": 匹配 a 后面跟0个或者一个"bc";
"a(bc){1,5}": 一个到5个 "bc."

还有一个字符 '│', 相当于OR 操作:

"hi│hello": 匹配含有"hi" 或者 "hello" 的 字符串;
"(b│cd)ef": 匹配含有 "bef" 或者 "cdef"的字符串;
"(a│b)*c": 匹配含有这样多个(包括0个)a或b,后面跟一个c
的字符串;

一个点('.')可以代表所有的单一字符,不包括"\n"

如果,要匹配包括"\n"在内的所有单个字符,怎么办?

对了,用'[\n.]'这种模式.

"a.[0-9]": 一个a加一个字符再加一个0到9的数字
"^.{3}$": 三个任意字符结尾 .

中括号括住的内容只匹配一个单一的字符

"[ab]": 匹配单个的 a 或者 b ( 和 "a│b" 一样);
"[a-d]": 匹配'a' 到'd'的单个字符 (和"a│b│c│d" 还有 "[abcd]"效果一样); 一般我们都用[a-zA-Z]来指定字符为一个大小写英文
"^[a-zA-Z]": 匹配以大小写字母开头的字符串
"[0-9]%": 匹配含有 形如 x% 的字符串
",[a-zA-Z0-9]$": 匹配以逗号再加一个数字或字母结尾的字符串

你也可以把你不想要得字符列在中括号里,你只需要在总括号里面使用'^' 作为开头 "%[^a-zA-Z]%" 匹配含有两个百分号里面有一个非字母的字符串.
要点:^用在中括号开头的时候,就表示排除括号里的字符

为了PHP能够解释,你必须在这些字符面前后加'',并且将一些字符转义.

不要忘记在中括号里面的字符是这条规路的例外―在中括号里面, 所有的特殊字符,包括(''), 都将失去他们的特殊性质 "[*\+?{}.]"匹配含有这些字符的字符串.

还有,正如regx的手册告诉我们: "如果列表里含有 ']', 最好把它作为列表里的第一个字符(可能跟在'^'后面). 如果含有'-', 最好把它放在最前面或者最后面, or 或者一个范围的第二个结束点[a-d-0-9]中间的‘-’将有效.

看了上面的例子,你对{n,m}应该理解了吧.要注意的是,n和m都不能为负整数,而且n总是小于m. 这样,才能 最少匹配n次且最多匹配m次. 如"p{1,5}"将匹配 "pvpppppp"中的前五个p

下面说说以\开头的

\b 书上说他是用来匹配一个单词边界,就是...比如've\b',可以匹配love里的ve而不匹配very里有ve

\B 正好和上面的\b相反.例子我就不举了

.....突然想起来....可以到http://www.phpv.net/article.php/251 看看其它用\ 开头的语法

好,我们来做个应用:

如何构建一个模式来匹配 货币数量 的输入

构建一个匹配模式去检查输入的信息是否为一个表示money的数字。我们认为一个表示money的数量有四种方式: "10000.00" 和 "10,000.00",或者没有小数部分, "10000" and "10,000". 现在让我们开始构建这个匹配模式:

^[1-9][0-9]*$

这是所变量必须以非0的数字开头.但这也意味着 单一的 "0" 也不能通过测试. 以下是解决的方法:

^(0│[1-9][0-9]*)$

"只有0和不以0开头的数字与之匹配",我们也可以允许一个负号在数字之前:

^(0│-?[1-9][0-9]*)$

这就是: "0 或者 一个以0开头 且可能 有一个负号在前面的数字." 好了,现在让我们别那么严谨,允许以0开头.现在让我们放弃 负号 , 因为我们在表示钱币的时候并不需要用到. 我们现在指定 模式 用来匹配小数部分:

^[0-9]+(\.[0-9]+)?$

这暗示匹配的字符串必须最少以一个阿拉伯数字开头. 但是注意,在上面模式中 "10." 是不匹配的, 只有 "10" 和 "10.2" 才可以. (你知道为什么吗)

^[0-9]+(\.[0-9]{2})?$

我们上面指定小数点后面必须有两位小数.如果你认为这样太苛刻,你可以改成:

^[0-9]+(\.[0-9]{1,2})?$

这将允许小数点后面有一到两个字符. 现在我们加上用来增加可读性的逗号(每隔三位), 我们可以这样表示:

^[0-9]{1,3}(,[0-9]{3})*(\.[0-9]{1,2})?$

不要忘记 '+' 可以被 '*' 替代 如果你想允许空白字符串被输入话 (为什么?). 也不要忘记反斜杆 ’\’ 在php字符串中可能会出现错误 (很普遍的错误).

现在,我们已经可以确认字符串了, 我们现在把所有逗号都去掉 str_replace(",", "", $money) 然后在把类型看成 double然后我们就可以通过他做数学计算了.

再来一个:

构造检查email的正则表达式

在一个完整的email地址中有三个部分: 
1. 用户名 (在 '@' 左边的一切),
2.'@',
3. 服务器名(就是剩下那部分).
用户名可以含有大小写字母阿拉伯数字,句号 ('.'), 减号('-'), and 下划线 ('_'). 服务器名字也是符合这个规则,当然下划线除外.

现在, 用户名的开始和结束都不能是句点. 服务器也是这样. 还有你不能有两个连续的句点他们之间至少存在一个字符,好现在我们来看一下怎么为用户名写一个匹配模式:

^[_a-zA-Z0-9-]+$

现在还不能允许句号的存在. 我们把它加上:

^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*$

上面的意思就是说: "以至少一个规范字符(除了.)开头,后面跟着0个或者多个以点开始的字符串."

简单化一点, 我们可以用 eregi()取代 ereg().eregi()对大小写不敏感, 我们就不需要指定两个范围 "a-z" 和 "A-Z"

http://www.phpv.net/html/1415.html

Javascript经典正则表达式

三道测试题:
1, var str=“ abbbbacc”;
var rs=str.replace(“a”,”0”);   
(A)0bbbbacc (B)0bbbb0cc
2,var str=“ abbbbacc”;
var rs=str.replace(“/a/”,”0”);   
(A)0bbbbacc (B)0bbbb0cc
3,var str=“ abbbbacc”;
var rs=str.replace(“/a/g”,”0”);   
(A)0bbbbacc (B)0bbbb0cc
附注:String对象的replace方法签名为:
replace(regx,str)
一,概述

1,正则表达式,可以说是任何一种编程语言都提供的机制,它主要是提供了对字符串的处理能力。
2,正则表达式在页面处理中的使用场景:
1)表单验证。验证某些域符合某种规则,例如邮件输入框必须输入的是邮件、联系电话输入框输入的必须是数字等等
2)处理DOM模型。例如通过表达式定位DOM中的一个对象或一系列对象,一个例子就是定位id属性中含有某个特殊字符的div对象。
3)纯编程逻辑。直接用于编程的逻辑之中。
3,说明:本部分所举的正则表达式的代码片断,都是经过测试的,但有一点需要注意,对于换行的字符串的定义,我们在表述时使用的是类似如下的形式:
var str=“It’s is
a  beautiful city”;
这种形式直接写在JS代码中是错误的,那如何获取具有换行的字符串呢?简单的办法:在textarea中输入文本并换行,然后将该值赋给JS变量即可。例如:
var str=document.forms[0].mytextarea.value;         

二,语法与使用

1,定义正则表达式

1)定义正则表达式有两种形式,一种是普通方式,一种是构造函数方式。
2)普通方式:var reg=/表达式/附加参数
表达式:一个字符串,代表了某种规则,其中可以使用某些特殊字符,来代表特殊的规则,后面会详细说明。
附加参数:用来扩展表达式的含义,目前主要有三个参数:
g:代表可以进行全局匹配。
i:代表不区分大小写匹配。
m:代表可以进行多行匹配。
上面三个参数,可以任意组合,代表复合含义,当然也可以不加参数。
例子:
var reg=/a*b/;
var reg=/abc+f/g;
3)构造函数方式:var reg=new RegExp(“表达式”,”附加参数”);
其中“表达式”与“附加参数”的含义与上面那种定义方式中的含义相同。
例子:
var reg=new RegExp(“a*b”);
var reg=new RegExp(“abc+f”,”g”);
4)普通方式与构造函数方式的区别
普通方式中的表达式必须是一个常量字符串,而构造函数中的表达式可以是常量字符串,也可以是一个js变量,例如根据用户的输入来作为表达式参数等等:
var reg=new RegExp(document.forms[0].exprfiled.value,”g”);

2,表达式模式

1)表达式模式,是指表达式的表达方式与样式, 即 var reg=/表达式/附加参数 中的“表达式”怎样去描述?
2)从规范上讲,表达式模式分为简单模式和复合模式。
3)简单模式:是指通过普通字符的组合来表达的模式,例如
var reg=/abc0d/;
可见简单模式只能表示具体的匹配。
4)复合模式:是指含有通配符来表达的模式,例如:
var reg=/a+b?\w/;
其中的+、?和\w都属于通配符,代表着特殊的含义。因此复合模式可以表达更为抽象化的逻辑。
下面我们着重说一下复合模式中各个通配符的含义及其使用。
5)复合模式中特殊字符的讲解:

1>\:在许多编程语言里面被用作转义符,一般来说
\符号后面如果跟的是普通字符c,那么\c就代表特殊的含义,例如n本来代表字符n,但\n就代表换行。
\符号后面如果跟的是特殊字符c,那么\c就代表普通字符c,例如\一般用作转义符,但\\则调表普通字符\。
Javascript的正则表达式中\的用法与上面相同,只是不同的编程语言,特殊字符表可能不太一样罢了。

2>^:匹配输入字符串的起始端,如果是多行匹配,即表达式的附加参数中含有m,则也在一个换行符后匹配。
例子:/^B/匹配 “Bab Bc ”中的第一个B
例子2:/^B/gm匹配
          “Badd B
          cdaf
          B dsfB”
          中的第一行第一个B,第三行中的第一个B
3>$:匹配输入字符创的尾端,如果是多行匹配,即表达式的附加参数中含有m,则也在一个换行符前匹配。
与^的用法相反。
例子:/t$/匹配“bat”中的t,但是不匹配“hate”中的t
例子2:/t$/匹配
“tag at
bat”
中第一行的最后一个t和第二行的t。

4>*:匹配前一个字符0次或多次。
例子:/ab*/匹配“dddabbbbc”中的“abbbb”,也匹配“ddda”中的“a”

5>+:匹配前一个字符1次或多次。
例子:/ab+/匹配“dddabbbbc”中的“abbbb”,但不匹配“ddda”
与后面的{1,}(原型:{n,})的用法类似

6>?:?的用法比较特殊,一般来说它用来对前一个字符做0次或1次匹配,但是它有另外两种特殊的用法:
如果紧跟在*、+、?和{ }之后,则表示原始匹配的最小次数匹配,例如:
/ba*/本来匹配“bbbaaaa”中的“baaaa”,但是/ba*?/则匹配“bbbaaaa”中的“b”(因为*表示0次或多次匹配,而加?应该表示最少次数匹配,即0次匹配)。
同理:/ba+?/则匹配“baaaa”中的“ba”。
作为语法结构符号,使用于前置断言中,即后面要说到的x(?=y)和x(?!=y)

7>.:小数点中的“.”号,匹配任何一个单独的字符,但是换行符除外。
标准中总共有哪些字符?请参考:字符集
例如:/a.b/匹配“acbaa”中的“acb”,但是不匹配“abbb”。

8>(x):表示匹配x(并非特指字符x或者特指一个字符,x表示一个字符串),而且匹配会被记住,在语法中这种()被称为“capturing parentheses ”,即捕捉用的小括号。
匹配会被记住,是因为在表达式提供的函数中,有些函数返回一个数组,该数组会保存所匹配的所有字符串,例如exec()函数。
另外还要注意()中的x被记住的前提是匹配x。
例子1:
var regx=/a(b)c/;
var rs=regx.exec(“abcddd”);
从上面可以看出,/a(b)c/匹配“abcddd”中的“abc”,因为()的原因,b也会记录下来,因此rs返回的数字内容为:
{abc,b}
例子2:
var regx=/a(b)c/;
var rs=regx.exec(“acbcddd”);
rs返回null,因为/a(b)c/不匹配“acbcddd”,所以()中的b不会被记录下来(尽管字符串中含有b)
9>(?:x):匹配x,但不会记住x,这种格式中的()被称为“non-capturing parentheses ”,即非捕捉用的小括号。
例子:
var regx=/a(?:b)c/;
var rs=regx.exec(“abcddd”);
从上面可以看出,/a(?:b)c/匹配“abcddd”中的“abc”,因为(?:)的原因,b不会记录下来,因此rs返回的数字内容为:
{abc}

10>X(?=y):匹配x,仅当后面紧跟着y时。如果符合匹配,则只有x会被记住,y不会被记住。
例子:
var regx=/user(?=name)/;
var rs=regx.exec(“The username is Mary”);
结果:匹配成功,而且rs的值为{user}

11>X(?!y):匹配x,仅当后面不紧跟着y时。如果符合匹配,则只有x会被记住,y不会被记住。
例子:
var regx=/user(?!name)/;
var rs=regx.exec(“The user name is Mary”);
结果:匹配成功,而且rs的值为{user}
例子2:
var regx=/\d+(?!\.)/;
var rs=regx.exec(“54.235”);
结果:匹配成果,rs的值为{5},不匹配54是因为54后面跟着“.”号,当然235也匹配,但是由于exec方法的行为,235不会被返回

12>x|y:匹配x或y。注意如果x和y都匹配上了,那么只记住x。
例子:
var regx=/beijing|shanghai/;
var rs=regx.exec(“I love beijing and shanghai”);
结果:匹配成功,rs的值为{beijing},虽然shanghai也匹配,但不会被记住。

13>{n}:匹配前一个字符的n次出现。
n必须是一个非负数,当然如果是一个负数或小数也不会报语法错误。
例子:
var regx=/ab{2}c/;
var rs=regx.exec(“abbcd”);
结果:匹配成功,rs的值为:{abbc}。

14>{n,}:匹配前一个字符的至少n次出现。
例子:
var regx=/ab{2,}c/;
var rs=regx.exec(“abbcdabbbc”);
结果:匹配成功,rs的值为:{abbc}。注意为什么abbbc也符合条件为什么没有被记住,这与exec方法的行为有关,后面会统一讲解。

15>{n,m}:匹配前一个字符的至少n次最多m次的出现。
只要n与m为数字,而且m>=n就不会报语法错误。
例子:
var regx=/ab{2,5}c/;
var rs=regx.exec(“abbbcd”);
结果:匹配成功,rs的值为:{abbbc}。
例子2:
var regx=/ab{2,2}c/;
var rs=regx.exec(“abbcd”);
结果:匹配成功,rs的值为:{abbc}。
例子3:
var regx=/ab(2,5)/;
var rs=regx.exec(“abbbbbbbbbb”);
结果:匹配成功,rs的值为:{abbbbb},这说明,如果前一个字符出现多于m次,则只匹配m次。另外:
var regx=/ab(2,5)c/;
var rs=regx.exec(“abbbbbbbbbbc”);
结果:匹配失败,rs的值为:null,为什么匹配失败,因为b多于5个则b(2,5)会匹配前5个b,,而表达式/ab(2,5)c/中b后面是c,但字符串中5个b之后还是b所以会报错。

16>[xyz]:xyz表示一个字符串,该模式表示匹配[]中的一个字符,形式上[xyz]等同于[x-z]。
例子:
var regx=/a[bc]d/;
var rs=regx.exec(“abddgg”);
结果:匹配成功,rs的值为:{abd}
例子2:
var regx=/a[bc]d/;
var rs=regx.exec(“abcd”);
结果:匹配失败,rs的值为:null,之所以失败,是因为[bc]表示匹配b或c中的一个,但不会同时匹配。

17>[^xyz]:该模式表示匹配非[]中的一个字符,形式上[^xyz]等同于[^x-z]。
例子:
var regx=/a[^bc]d/;
var rs=regx.exec(“afddgg”);
结果:匹配成功,rs的值为:{afd}
例子2:
var regx=/a[^bc]d/;
var rs=regx.exec(“abd”);
结果:匹配失败,rs的值为:。

18>[\b]:匹配退格键。

19>\b:匹配一个词的边界符,例如空格和换行符等等,当然匹配换行符时,表达式应该附加参数m。
例子:
var regx=/\bc./;
var rs=regx.exec(“Beijing is a beautiful city”);
结果:匹配成功,rs的值为:{ci},注意c前边的空格不会匹配到结果中,即{ ci}是不正确的。

20>\B:代表一个非单词边界。
例子:
var regx=/\Bi./;
var rs=regx.exec(“Beijing is a beautiful city”);
结果:匹配成功,rs的值为:{ij},即匹配了Beijing中的ij。

21>\cX,匹配一个控制字符。例如, \cM 匹配一个 Control-M 或
回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一
个原义的 ’c’ 字符。(实际的例子还需补充)

21>\d:匹配一个数字字符,等同于[0-9]。
例子:
var regx=/user\d/;
var rs=regx.exec(“user1”);
结果:匹配成功,rs的值为:{user1}

22>\D:匹配一个非数字字符,等同于[^0-9]。
例子:
var regx=/user\D/;
var rs=regx.exec(“userA”);
结果:匹配成功,rs的值为:{userA}

23>\f:匹配一个换页符。

24>\n:匹配一个换行符。因为是换行符,所以在表达式中要加入m参数。
例子:
var regx=/a\nbc/m;
       var str=“a
               bc”;
       var rs=regx.exec(str);
       结果:匹配成功,rs的值为:{  },如果表达式为/a\n\rbc/,则不会被匹配,因此在一般的编辑器中一个”Enter”键代表着“回车换行”,而非“换行回车”,至少在textarea域中是这样的。      
25>\r:匹配一个回车符

26>\s:匹配一个空格符,等同于[ \f\n\r\t\v\u00A0\u2028\u2029].
例子:
var regx=/\si/;
var rs=regx.exec(“Beijing is a city”);
结果:匹配成功,rs的值为:{ i}

27>\S:匹配一个非空格符,等同于[ ^\f\n\r\t\v\u00A0\u2028\u2029].
例子:
var regx=/\Si/;
var rs=regx.exec(“Beijing is a city”);
结果:匹配成功,rs的值为:{ei}

28>\t:匹配一个tab
例子:
var regx=/a\tb/;
var rs=regx.exec(“a bc”);
结果:匹配成功,rs的值为: {a       bc}

29>\v:匹配一个竖向的tab

30>\w:匹配一个数字、_或字母表字符,即[A-Za-z0-9_ ]。
例子:
var regx=/\w/;
var rs=regx.exec(“$25.23”);
结果:匹配成功,rs的值为:{2}

31>\W:匹配一个非数字、_或字母表字符,即[^A-Za-z0-9_ ]。
例子:
var regx=/\w/;
var rs=regx.exec(“$25.23”);
结果:匹配成功,rs的值为:{$}

32>\n:注意不是\n,这里n是一个正整数,表示匹配第n个()中的字符。
例子:
var regx=/user([,-])group\1role/;
var rs=regx.exec(“user-group-role”);
结果:匹配成功,rs的值为:{user-group-role,-},同样对user,group,role的匹配也是成功的,但像user-group,role等就不对了。

33>\0:匹配一个NUL字符。

34>\xhh:匹配一个由两位16进制数字所表达的字符。

35>\uhhhh:匹配一个由四位16进制数字所表达的字符。

3,表达式操作

1)表达式操作,在这里是指和表达式相关的方法,我们将介绍六个方法。
2)表达式对象(RegExp)方法:

1>exec(str),返回str中与表达式相匹配的第一个字符串,而且以数组的形式表现,当然如果表达式中含有捕捉用的小括号,则返回的数组中也可能含有()中的匹配字符串,例如:
var regx=/\d+/;
var rs=regx.exec(“3432ddf53”);
返回的rs值为:{3432}
var regx2=new RegExp(“ab(\d+)c”);
var rs2=regx2.exec(“ab234c44”);
返回的rs值为:{ab234c,234}
另外,如果有多个合适的匹配,则第一次执行exec返回一个第一个匹配,此时继续执行exec,则依次返回第二个第三个匹配。例如:
var regx=/user\d/g;
var rs=regx.exec(“ddduser1dsfuser2dd”);
var rs1=regx.exec(“ddduser1dsfuser2dd”);
则rs的值为{user1},rs的值为{rs2},当然注意regx中的g参数是必须的,否则无论exec执行多少次,都返回第一个匹配。后面还有相关内容涉及到对此想象的解释。

2>test(str),判断字符串str是否匹配表达式,返回一个布尔值。例如:
var regx=/user\d+/g;
var flag=regx.test(“user12dd”);
flag的值为true。

3)String对象方法

1>match(expr),返回与expr相匹配的一个字符串数组,如果没有加参数g,则返回第一个匹配,加入参数g则返回所有的匹配
例子:
var regx=/user\d/g;
var str=“user13userddduser345”;
var rs=str.match(regx);
rs的值为:{user1,user3}

2>search(expr),返回字符串中与expr相匹配的第一个匹配的index值。
例子:
var regx=/user\d/g;
var str=“user13userddduser345”;
var rs=str.search(regx);
rs的值为:0

3>replace(expr,str),将字符串中匹配expr的部分替换为str。另外在replace方法中,str中可以含有一种变量符号$,格式为$n,代表匹配中被记住的第n的匹配字符串(注意小括号可以记忆匹配)。
例子:
var regx=/user\d/g;
var str=“user13userddduser345”;
var rs=str.replace(regx,”00”);
rs的值为:003userddd0045
例子2:
var regx=/u(se)r\d/g;
var str=“user13userddduser345”;
var rs=str.replace(regx,”$1”);
rs的值为:se3userdddse45
对于replace(expr,str)方法还要特别注意一点,如果expr是一个表达式对象则会进行全局替换(此时表达式必须附加参数g,否则也只是替换第一个匹配),如果expr是一个字符串对象,则只会替换第一个匹配的部分,例如:
var regx=“user”
var str=“user13userddduser345”;
var rs=str.replace(regx,”00”);
rs的值为: 0013userddduser345

4>split(expr),将字符串以匹配expr的部分做分割,返回一个数组,而且表达式是否附加参数g都没有关系,结果是一样的。
例子:
var regx=/user\d/g;
var str=“user13userddduser345”;
var rs=str.split(regx);
rs的值为:{3userddd,45}

4,表达式相关属性

1)表达式相关属性,是指和表达式相关的属性,如下面的形式:
var regx=/myexpr/;
var rs=regx.exec(str);
其中,和表达式自身regx相关的属性有两个,和表达式匹配结果rs相关的属性有三个,下面将逐一介绍。
2)和表达式自身相关的两个属性:

1>lastIndex,返回开始下一个匹配的位置,注意必须是全局匹配(表达式中带有g参数)时,lastIndex才会有不断返回下一个匹配值,否则该值为总是返回第一个下一个匹配位置,例如:
var regx=/user\d/;
var rs=regx.exec(“sdsfuser1dfsfuser2”);
var lastIndex1=regx.lastIndex;
rs=regx.exec(“sdsfuser1dfsfuser2”);
var lastIndex2=regx.lastIndex;
rs=regx.exec(“sdsfuser1dfsfuser2”);
var lastIndex3=regx.lastIndex;
上面lastIndex1为9,第二个lastIndex2也为9,第三个也是9;如果regx=/user\d/g,则第一个为9,第二个为18,第三个为0。

2>source,返回表达式字符串自身。例如:
var regx=/user\d/;
var rs=regx.exec(“sdsfuser1dfsfuser2”);
var source=regx.source;
source的值为user\d
3)和匹配结果相关的三个属性:

1>index,返回当前匹配的位置。例如:
var regx=/user\d/;
var rs=regx.exec(“sdsfuser1dfsfuser2”);
var index1=rs.index;
rs=regx.exec(“sdsfuser1dfsfuser2”);
var index2=rs.index;
rs=regx.exec(“sdsfuser1dfsfuser2”);
var index3=rs.index;
index1为4,index2为4,index3为4,如果表达式加入参数g,则index1为4,index2为13,index3会报错(index为空或不是对象)。

2>input,用于匹配的字符串。例如:
var regx=/user\d/;
var rs=regx.exec(“sdsfuser1dfsfuser2”);
var input=rs.input;
input的值为sdsfuser1dfsfuser2。

3>[0],返回匹配结果中的第一个匹配值,对于match而言可能返回一个多值的数字,则除了[0]外,还可以取[1]、[2]等等。例如:
var regx=/user\d/;
var rs=regx.exec(“sdsfuser1dfsfuser2”);
var value1=rs[0];
rs=regx.exec(“sdsfuser1dfsfuser2”);
var value2=rs[0];
value1的值为user1,value2的值为user2

5,实际应用

1)实际应用一
描述:有一表单,其中有一个“用户名”input域
要求:汉字,而且不能少于2个汉字,不能多于4个汉字。
实现:
<script>
function checkForm(obj){
     var username=obj.username.value;
     var regx=/^[\u4e00-\u9fa5]{2,4}$/g
     if(!regx.test(username)){
               alert(“Invalid username!”);
               return false;
     }
     return true;
}
</script>
<form name=“myForm”onSubmit=“return checkForm(this)”>
    <input type=“text” name=“username”/>
    <input type=“submit” vlaue=“submit”/>
</form>
2)实际应用二
描述:给定一个含有html标记的字符串,要求将其中的html标记去掉。
实现:
<script>
function toPlainText(htmlStr){
     var regx=/<[^>]*>|<\/[^>]*>/gm;
     var str=htmlStr.replace(regx,"");
     return str;
}
</script>
<form name=“myForm”>
    <textarea id=“htmlInput”></textarea>
    <input type=“button” value=“submit” onclick=“toPlainText(document.getElementById(‘htmlInput’).value”/>
</form>

三,小结

1,Javascript正则表达式,我想在一般的程序员之中,使用者应该不是很多,因为我们处理的页面一般都不是很复杂,而复杂的逻辑一般我们都在后台处理完成了。但是目前趋势已经出现了扭转,富客户端已经被越来越多的人接受,而Javascript就是其中的关键技术,对于复杂的客户端逻辑而言,正则表达式的作用也是很关键的,同时它也是Javascript高手必须要掌握的重要技术之一。

2,为了能够便于大家对前面讲述的内容有一个更为综合和深刻的认识,我将前面的一些关键点和容易犯糊涂的地方再系统总结一下,这部分很关键!
总结1:附件参数g的用法
表达式加上参数g之后,表明可以进行全局匹配,注意这里“可以”的含义。我们详细叙述:
1)对于表达式对象的exec方法,不加入g,则只返回第一个匹配,无论执行多少次均是如此,如果加入g,则第一次执行也返回第一个匹配,再执行返回第二个匹配,依次类推。例如
var regx=/user\d/;
var str=“user18dsdfuser2dsfsd”;
var rs=regx.exec(str);//此时rs的值为{user1}
var rs2=regx.exec(str);//此时rs的值依然为{user1}
如果regx=/user\d/g;则rs的值为{user1},rs2的值为{user2}
通过这个例子说明:对于exec方法,表达式加入了g,并不是说执行exec方法就可以返回所有的匹配,而是说加入了g之后,我可以通过某种方式得到所有的匹配,这里的“方式”对于exec而言,就是依次执行这个方法即可。
2)对于表达式对象的test方法,加入g于不加上g没有什么区别。
3)对于String对象的match方法,不加入g,也只是返回第一个匹配,一直执行match方法也总是返回第一个匹配,加入g,则一次返回所有的匹配(注意这与表达式对象的exec方法不同,对于exec而言,表达式即使加上了g,也不会一次返回所有的匹配)。例如:
var regx=/user\d/;
var str=“user1sdfsffuser2dfsdf”;
var rs=str.match(regx);//此时rs的值为{user1}
var rs2=str.match(regx);//此时rs的值依然为{user1}
如果regx=/user\d/g,则rs的值为{user1,user2},rs2的值也为{user1,user2}
4)对于String对象的replace方法,表达式不加入g,则只替换第一个匹配,如果加入g,则替换所有匹配。(开头的三道测试题能很好的说明这一点)
5)对于String对象的split方法,加上g与不加g是一样的,即:
var sep=/user\d/;
var array=“user1dfsfuser2dfsf”.split(sep);
则array的值为{dfsf, dfsf}
此时sep=/user\d/g,返回值是一样的。
6)对于String对象的search方法,加不加g也是一样的。
总结2:附加参数m的用法
附加参数m,表明可以进行多行匹配,但是这个只有当使用^和$模式时才会起作用,在其他的模式中,加不加入m都可以进行多行匹配(其实说多行的字符串也是一个普通字符串),我们举例说明这一点
1)使用^的例子
var regx=/^b./g;
var str=“bd76 dfsdf
         sdfsdfs dffs
         b76dsf  sdfsdf”;
var rs=str.match(regx);
此时加入g和不加入g,都只返回第一个匹配{bd},如果regx=/^b./gm,则返回所有的匹配{bd,b7},注意如果regx=/^b./m,则也只返回第一个匹配。所以,加入m表明可以进行多行匹配,加入g表明可以进行全局匹配,综合到一起就是可以进行多行全局匹配
2)使用其他模式的例子,例如
var regx=/user\d/;
var str=“sdfsfsdfsdf
         sdfsuser3 dffs
         b76dsf  user6”;
var rs=str.match(regx);
此时不加参数g,则返回{user3},加入参数g返回{user3,user6},加不加入m对此没有影响。
3)因此对于m我们要清楚它的使用,记住它只对^和$模式起作用,在这两种模式中,m的作用为:如果不加入m,则只能在第一行进行匹配,如果加入m则可以在所有的行进行匹配。我们再看一个^的例子
var regx=/^b./;
var str=“ret76 dfsdf
         bjfsdfs dffs
         b76dsf  sdfsdf”;
var rs=str.match(regx);
此时rs的值为null,如果加入g,rs的值仍然为null,如果加入m,则rs的值为{bj}(也就是说,在第一行没有找到匹配,因为有参数m,所以可以继续去下面的行去找是否有匹配),如果m和g都加上,则返回{bj,b7}(只加m不加g说明,可以去多行进行匹配,但是找到一个匹配后就返回,加入g 表明将多行中所有的匹配返回,当然对于match方法是如此,对于exec呢,则需要执行多次才能依次返回)
总结3:在HTML的textarea输入域中,按一个Enter键,对应的控制字符为“\r\n”,即“回车换行”,而不是“\n\r”,即“换行回车”,我们看一个前面我们举过的例子:
var regx=/a\r\nbc/;
var str=“a
         bc”;
var rs=regx.exec(str);
结果:匹配成功,rs的值为:{      },如果表达式为/a\n\rbc/,则不会被匹配,因此在一般的编辑器中一个”Enter”键代表着“回车换行”,而非“换行回车”,至少在textarea域中是这样的。

四,应用案例

1,正则表达式使用场景:
1)登录场景,检查用户输入的用户名,要求:
字符长度在6到18之间
字符必须为字母、数字或者下划线的组合
2)购物场景,对于商品列表的描述(标签、条形码、单价)等有些用户可能会提出下面需求:
希望可以对商品列表的某一列描述进行字号的自定义,而且下次登录仍然保持用户的修改
2,下面看一下在上述两个场景中,正则表达式的使用方式和具体实现。
1)登录场景部分正则表达式实现

2)购物场景部分正则表达式实现
1>需求分析:用户希望可以对商品列表的某一列描述进行字号的自定义,而且下次登录仍然保持用户的修改

2>程序设计

类图:

3>代码实现
PageSetting类

PageSettingParser类

注:如果需要上面的应用案例的工程,请发email给我。

http://www.phpv.net/html/1534.html

2008年8月27日

當造貝食--八月,澳洲蠔發威

第 682 期大食男女  主題飲食
當造貝食【八月,澳洲蠔發威】
clip_image001
子曰:不時不食。
老人家金口一開,就成了千百年來老饕追逐的金科玉律。
那,此時,此地,甚麼最當造?答案是:貝類。
夏季的高溫,曬得海水暖和,暖和的海水,滋養大量微生物,靠吃牠們長大的貝類也變得特別肥美。
蠔、蜆、螺、青口、帶子、扇貝、鮑魚……全都預備好,都在最佳狀態了。
就等你金口一開,把鮮爽甜全部吃進肚裏。貝之種類貝類品種繁多,全世界有數萬種,但可供食用的主要有兩大科,雙殼類和腹足類。雙殼有兩個外殼,活動較少,多在沙泥底或依附在礁石上生活,它們肉質軟腍,味道清鮮,代表的有蠔、蜆、青口、帶子、扇貝。
腹足類,是單殼的,以露出的腹部肌肉吸啜爬行,多生活在沙泥面或珊瑚之間,它們會四周覓食,主要吃海洋生物或植物維生,活動多,肉質爽口煙韌,味道甘甜,代表的有鮑魚與螺類。Tasmanian$32/隻 有海水味,也略帶甜味,蠔味偏淡,肉質爽滑。
clip_image003
SmokyBay$32/隻 鹹度較低,爽口脆身、有像水果的香味。
clip_image004
Pittwater$32/隻 中度海水味,口感腍滑,蠔味濃。
clip_image005
StPeterIsland$32/隻 鹹味中等,吃罷回甘,鮮味也濃。
clip_image006
FranklinHarbour$32/隻 海水味頗強,肉質很爽口,裙邊鮮甜。
clip_image007
SydneyRock$38/隻 有很濃郁海水味,肉滑而腍,餘韻帶甜。
clip_image008
Angassi$60/隻 有重重海水、金屬味,是澳洲蠔之中味道最濃的。
clip_image009
在貝類中,蠔之家族幅員最廣闊,歐洲、亞洲、大洋洲、美加都有它們的種族。
蠔,味道有濃有淡,口感有實有軟,外形有肥有瘦。但不論品種和產地,時令還是最重要,夏季蠔還未吸盡養分過冬,故瘦削味不濃。
到了秋冬才是蠔最靚的季節,南半球的澳洲,正值冬天,吃蠔最合時。辣汁、蠔汁、檸檬全部齊全。
clip_image010
投進海洋滋味8月,是澳洲蠔一年一度發威的日子。
因為8月的歐洲還是夏天,蠔瘦而味淡,論質素,怎比得上正值冬天的澳洲蠔?
這時期的澳蠔,肥美肉厚,海水味重,鹹鮮味濃,放進口,味蕾就像在大海中暢游。
西澳、南澳及塔斯曼尼亞都產蠔,但由於水域的礦物質及食物不同,澳洲蠔一樣有不同味道,有不同身價。澳洲蠔主要在海中養殖,七月開始收成。
clip_image011
Oyster&WineBar的蠔很新鮮,客人在蠔吧坐,可看到師傅開蠔。
clip_image013
矜貴澳洲Belon在眾多澳洲蠔之中,我最愛就是Angassi,人們喜歡叫它做澳洲Belon,因為它與法國Belon同屬銅蠔科,同樣外形扁平,同樣有很重的礦物味,但Angassi還多了一朕濃濃的鹹鮮。
鹹鮮味來自它全時間養在海中,不像法國Belon,會在河流及海中輪流養,蠔味較淡。
不過香港人吃慣法國Belon,很少蠔店會入口Angassi,Oyster&WineBar是例外。
「Angassi的礦物味冇法國Belon咁濃,好多人因為咁覺得佢冇咁好,但係食真的,你會發覺佢鮮味強好多,aftertaste更加持久!」總廚Oscar說。
我絕對同意!
如果真的怕海水味太重,其實可以滴少許檸檬汁,讓酸味中和其鹹味。
另外,也可以用味道較重的白酒去平衡,法國的ChablisAppellation2004是個不錯的選擇。全港最齊款澳洲蠔除了Angassi之外,這裏的澳洲蠔也是全港最多的,每日平均有六七款,比坊間一般兩三款多,包括有最多人認識的Tasmanian、Sydney、Rock,另外也有較少人知的StPeterlsland、FranklinHarbour等。跟Angassi一樣,這些蠔一星期來貨四次,七至九月的澳洲蠔當造期內,不論哪一天來吃,都非常新鮮。Oyster&WineBar地址:尖沙咀彌敦道20號喜來登酒店18樓 電話:23691111

派出所开荒种菜养鸡

成都锦江一派出所开荒种菜养鸡 被质疑不务正业()

新闻来源: 新华网 于August 27, 2008 03:47:37

 Img259232901

派出所民警利用业余时间收玉米

Img259232902

派出所大厨樊师傅在喂鸡

Img259232903

民警拿着自己种的玉米,很有成就感

成都市锦江公安分局成龙路派出所在房前屋后垦荒种菜引发争议面对质疑,该所称并没荒废警务

“派出所将在今年春节前完成搬迁,到时警察农庄将铁定成为历史。”昨日,成都市锦江公安分局成龙路派出所教导员陈芮,站在自己办公室的窗前,神情有些落寞。窗外,一大片由派出所警察开垦出的玉米地和辣椒林,长得正欢……陈芮说,随着派出所搬迁倒计时的临近,弥漫在他们中的离愁别绪也越来越重,“ 自家耕种的绿色蔬菜再也吃不到了!”

A“警察农庄的前尘往事

全民垦荒绿色蔬菜端上饭桌

被陈芮和其同事们视若珍宝的,正是派出所“房前屋后”的那一大片绿色庄园。“最多的时候差不多有2亩地,种了几十种蔬菜。”55岁的警察食堂厨师樊邦友,从郁郁葱葱的玉米地中探出脑袋对记者说。顺着他手指的方向看去,青砖黑瓦的两层派出所掩映在绿色丝瓜和豇豆环绕的坡地中,间或伴着几声鸡叫,颇有点“ 室外桃源”的味道。

陈芮说,2005年他们刚搬到三环路外的成龙路时,派出所门前只有一片荒凉的山坡地,“别说购物逛商场,就连买菜都要到二环路边的菜市去。”就在50多名警察面面相觑时,来自简阳平武镇的厨师樊邦友干了一件惊人之举,“他找到所长,自告奋勇提出要垦荒种田。”

在全所民警的支持下,50多名警察成了樊师傅垦荒的“农小兵”。“那段时间,你常常可以看到这样一种场景,”樊师傅抬起晒得通红的手臂,一边擦汗一边回忆。“天黑后,一些下了班的警察放弃休息,帮我搬石头,还有一些刚从警校毕业的女娃娃,也嘿咻嘿咻地扛起了锄头。”

派出所的全民“垦荒”很快有了结果。几个月后,樊邦友从地里抱回了一个重二三十公斤的冬瓜,专门为大伙做了一锅冬瓜汤,“那是我这辈子喝过的最好喝的冬瓜汤。”警察徐健告诉记者。

养生态鸡吃自种蔬菜香得很

变化可以从饭桌上看出来。三年后的成龙路派出所,民警饭桌上的菜,已从单一的冬瓜汤,演变出了更多花样。昨日,就有用自家玉米煮成的玉米粥,现摘现做的干煸扁豆和新鲜宰杀的热汁鸡等摆上了警察的餐桌。“我种的菜只用粪灌溉、绝对不用化肥,养的鸡从来没喂过饲料。”樊邦友在有些油腻的围裙上反复搓着手,笑得很憨厚。

樊师傅说,除近日出产的扁豆、玉米外,他炒菜用的菜油、淀粉甚至泡菜和野山椒也来自“警察农庄”,“去年,田里收了500 多公斤红苕,吃不完,就加工成了红苕淀粉。今年菜籽和小米椒又大丰收,这不,泡菜都泡了5大坛了……”樊师傅向记者介绍的同时,坐在记者身旁的警察一边把一盘泡豇豆推到记者面前,一边推销:“好吃……吃自家地里的绿色蔬菜,饭都要多吃两碗。”该民警说,他最喜欢吃樊师傅做的藿香冬瓜,藿香、冬瓜都是自家田里现种的,“没有农药和化肥,香得很。”

饭 后,樊师傅端着一碗剩饭,乐颠颠地将记者领到了他搭在派出所食堂侧角的鸡棚,四只白色大公鸡正在棚子里溜达,看到主人来了后,刚刚还在找虫子吃的大公鸡,一蹦一跳地拥了过来,“本来养了8只,敞放时跑掉了一只,其余的都用来改善伙食了。”一旁的派出所教导员陈芮向记者解释着,而此时,喂完鸡的樊师傅已在地里忙开了。

改善伙食每年还省五六千元

“来帮帮忙,玉米可以掰了,红辣椒也熟了。”樊师傅在田里大声吆喝着,几个还来不及洗碗的民警马上顶着烈日一路小跑来到地边。“樊师傅,紫色辣椒可以摘吗?”“老樊,玉米应该怎么掰?”“樊师,紫色辣椒为什么不能吃呢?”好脾气的樊邦友一一解释着,身旁的所长余碚军看着民警在田里忙碌,眼角含笑,“警察农庄不仅可以改善大伙儿伙食,还可以教会民警懂得珍惜和团结。”

余碚军说,警察群体作为公务员,一直在用纳税人的钱,但通过自己劳动垦荒种田以后,部分蔬菜(如豇豆、野山椒等)已可以自给,每年可为派出所省下五六千元的买菜经费,“这些都用作进一步改善民警伙食了。”余碚军强调,在开办“警察农庄”的同时,派出所的案侦工作也没有落下,“农庄平时都是樊师傅老两口在打理,只有垦荒和丰收时,民警才会抽空去劳动。另外,帮忙的民警仅限不当班的内勤民警,主管破案和社会面控制的刑警和巡警都不允许在执勤期间帮忙。”

对自己主动垦荒种田的原因,当过炊事兵的老农樊邦友回答得很朴实:“派出所给我和我老婆(50岁的肖坤蓝,负责厨房打杂和打扫派出所清洁)每人每月一千元,所长和警察们也不拿我当外人,对我很好……”

B“警察农庄”引发的争议

一个位于城乡结合部的派出所,利用地利之便,开荒种田外带养鸡;50多个平时只管办案的警察,居然挽起裤管,在田里忙上忙下,亲如一家……警察种菜,这究竟是一种进步还是不务正业?在锦江公安分局16个派出所中独树一帜的成龙路派出所的做法,是否值得在有条件的地方进行推广呢?社会各界对此各有各的看法。

争论一:警察种菜是不是不务正业?

派出所长:垦荒种田并没有荒废警务

“我们派出所由于偏僻的地理位置所限,周边没有一家餐馆,甚至连小卖部都没有,民警执行公务又常常半夜才回所,如果基本生活都得不到保障,那又如何谈得上全力办案呢?为让民警吃饱吃好,派出所想到了自给自足这个办法:凡出产自警察农庄的菜,民警都不得带回家。另外,种菜省下来的钱,都用作进一步改善民警 伙食,而且我们派出所没有收所上民警一分钱伙食费……当班民警不得参与种地垦荒,不务正业无从谈起。”——成龙路派出所所长余碚军附近群众:只要辖区治安好,无所谓

“据我了解他们在周边种植蔬菜,是炊事员没事时自己种的,和民警、派出所没啥子关系……再说他们种菜对老百姓也没有干扰,也没有污染和毒气什么的。我们这里周围比较平静,治安还是挺好 的。其实,只要辖区治安好,我觉得他们种不种菜无所谓。”——成龙路辖区村民李昌其市区居民:地在派出所门口不碍事

“有些不能理解,毕竟穿起警服耕地还是有些怪。不过,反正地就在派出所门口,遇到紧急情况应该不会耽误出警吧。”——成都市红星路居民刘小平

争论二:警察农庄是否值得推广?

社会学家:不违规不占上班时间就可以

“民警种菜,这确实是一个容易引起争议的事情,毕竟,警察的身份特殊。但我认为,只要种菜的耕地来源合法,不涉及违规侵占公共资源,只要警察没有在上班时间不务正业,利用自身条件改善生活就无可厚非。其实,利用单位周边条件耕地种菜、养花养草,在西部山区的一些单位里早已出现,我觉得在美化环境之外,它 也确实能带来一些物质上的收益,是件好事情。但由于在派出所外种菜的群体是社会关注度较高的警察,民众对他们的预期和要求要高许多,所以出现质疑声也很正常。”——四川大学社会学与心理学系主任、四川省社会学学会会长陈昌文教授

快评

警察种菜不妨多点宽容

清代书画大师郑板桥有句名言:“流自己的汗,吃自己的饭。”我想,成龙路派出所的警察就是在工作之余流自己的汗,种菜养鸡,既改善了生活,又节省了经费。如此一举几得的事,我们是应该多给予一些宽容。

开荒种地,实现自给自足,在近现代本身就有模范可循。著名诗人贺敬之创作的歌词《南泥湾》,就纵情歌颂了三五九旅在垦荒屯田中的英雄业绩。歌词称:“又战斗来又生产,三五九旅是模范。”尽管现在时代发展了,社会物质丰富了,但这些光荣传统并没有丢掉,至少从成龙路派出所的做法可以依稀看到当年的影子。

警察种菜养鸡,在我看来至少有以下好处:首先,能改善派出所工作人员的生活;其次,能节省伙食费用;这两点从警察的叙述和实际情况来看都有成效。

再次,警察下地干活,并没有占用正常的工作时间,这对警务和他们并没有造成损失。同时还能锻炼身体,增强体质,也是一种很好的引导,总比业余时间“砌长城”(搓麻将)让人觉得更健康一些。参加种菜养鸡等活动,既能保证吃得好,也能保证身体得到锻炼,体质好了,在执行警务工作时就有了身体保证,何乐而不为呢?

最后,警察参加体力劳动,也能让他们明白,“劳动果实”的来之不易,体验到“衣食父母”田间劳作的艰辛,增加对人民朴素的感情。他们在执行公务时,也许就能从更人性的角度出发。因此,让我们不妨对警察种菜多点宽容。

潮飲懷舊椰汁月賺18萬

壹盤生意
潮飲懷舊椰汁月賺18「椰汁大王」每間分店外,往往出現長長的人龍;大家正耐心等候即開即搾的椰汁。
clip_image001
炎炎夏日,香港街頭繼排隊面試入學、去漫畫展及買限量版奧運紀念鈔後,又再出現「排隊潮」;這次為的,是飲品店「椰汁大王」出品的椰汁。
策劃今次「椰汁風暴」的老闆黃志強,單純想尋回木頭車椰汁檔的童年回憶,就在旺角街頭賣起新鮮即搾的椰汁來。他想懷舊,MK人卻認為飲椰汁夠鮮夠「潮」,在「blog」界日講夜講,結果「椰汁大王」獨沽一味賣椰汁便贏盡潮人歡心;短短半年在旺角、深水clip_image002及葵芳連開四店,月賺十八萬元。店鋪主攻細杯、大杯及樽裝椰汁,售價分別為$10、$13及$18,比附近跟風的椰汁店貴兩、三元。
clip_image003
不要以為有芽、長鬚的老椰子是劣貨,其實正正相反,椰肉夠厚水分甜,最適合做椰汁。而輕身及椰殼有裂痕的,則為渣貨。
clip_image004
「我細細個已經好鍾意飲椰汁,那時每星期最少要飲三杯clip_image005!」今年已年近四十的黃志強,一提起他的兒時最愛,立時雙眼發光,手舞足蹈,流露出回味的樣子。
回首他和椰汁的緣分,始於三十多年前。那時候他家住葵涌,在旺角讀小學下午校,每天傍晚放學時又累又餓,還要步行二十分鐘到大角咀碼頭搭巴士,所以每經過塘尾道,見到不少擺賣小食的木頭車檔時,自不然食指大動。
「其中一檔木頭車係賣新鮮椰汁clip_image006,離遠已聽到攪拌機clip_image006[1]摩打聲,加埋飄來陣陣clip_image006[2]椰香,令人未行近已經有衝動想飲!」
一起放學的同學仔,都會用零用錢買汽水、魚蛋、碗仔翅等小食,但他卻獨沽一味,只愛椰汁的香濃美味,「clip_image007時每杯都係即叫即製,入面有少少碎冰夠晒冰凍,飲飲clip_image008又有椰絲,咬落有口感,每杯只係三毫子咋!」顧客李小姐是第一次幫襯「椰汁大王」,飲落認為夠香濃,並謂下次會再幫襯。
clip_image009
中年人的椰汁回憶時光流逝,現時的黃志強已經是一間米線鋪的老闆,而木頭車檔被政府取締後,他亦再無飲過童年最愛的椰汁。一年前,他向太太提及童年椰汁的美味,但原來她只飲過椰漿加水的「A貨椰汁」,黃志強於是到街市買來椰子,自己做起椰汁來,太太大嗌好味,令他萌起開檔念頭。「而家古老當時興,好似老牌歌星開完一次又一次演唱會都咁受歡迎,我諗應該有不少中年人都想飲番椰汁。」
今年三月,黃志強在旺角弼街開設第一間「椰汁大王」,產品分為十元的細杯,十三元大杯及十八元樽裝三種,另外亦有售產自泰國的椰青椰皇,每個分別賣十二元及十五元,給不愛椰汁的顧客多個選擇。
由於屬試驗性質,他最初只入一袋共四十五個的椰子,足夠製出椰汁二百杯,測試市場反應。「若果生意欠佳,我已諗好轉賣魚蛋同碗仔翅clip_image010。」結果頭三天已經賣過二百杯,黃志強信心即時「返晒clip_image011」。
新鮮、夠味,是一眾顧客對「椰汁大王」的普遍意見。黃志強早在開店前,已花了兩個月研製;他小時曾企定定在木頭車檔前等買椰汁,故記得師傅的步驟,「先要用刀背大力敲開椰子殼,椰肉用椰子刀起出,切條用清水洗淨,再將椰肉及水放進攪拌機攪拌,隔渣取出漿水後,加入一定分量的花奶、冰塊及冰糖攪拌即成。」老闆黃志強在開業初期日日切椰條,結果試過手抽筋痛致晚晚失眠。
clip_image012
供應商甄想記每天都運貨到椰汁大王四間分店,每間約要二百多個椰子,可製一千多杯椰汁。
clip_image013
馬來亞椰子最香椰子的質素,是椰汁好壞的關鍵所在。黃志強嘗過不同品種的椰子,認為馬來西亞出產的最好。「我專登去圖書館借書研究椰子品種,又請教過一個椰子批發商clip_image006[3]朋友。發現馬來西亞的椰子主要種在泥地,營養充足,雖然唔夠椰水但一定夠香;而泰國同菲律賓clip_image006[4]椰子,則多生在水邊,椰水足但無香味,最後我都係揀馬來西亞貨。」然而馬來西亞貨價格較泰國、菲律賓的貴兩至三成,一袋四十五個椰子便要四百元。
黃志強亦選用較大馬力的專業廚用攪拌機,將椰肉研磨得更幼細,以壓出更多的漿水。選好攪拌機,用上最靚椰子,黃志強曾廣邀親朋好友試飲,反應雖好,不過仍嫌「爭咁clip_image014」。「我一向用白開水溝椰肉做椰汁,但有次看見媽媽用椰子本身的椰子水煲雞,就嘗試用椰子水取代開水,最後才造出心目中的完美椰汁。」
現時只要上網搜尋「椰汁大王」關鍵字,便會見大量「blog友」當新奇飲品試飲及推介,由於反應熱烈,往往要排隊等十五至三十分鐘才買到。二十多歲的李先生,便抵不住「椰子飄香」而一星期幫襯兩、三次,「我試過等成半個鐘先買到一杯,但clip_image014[1]椰汁真係好鮮甜,又有椰味,等耐clip_image014[2]都無所謂。」「椰汁大王」遂乘勢擴張,除了弼街總店外,另開三間分店於旺角水渠道、深水clip_image002[1]桂林街及葵芳的葵涌廣場。四間店的共通點,都是近地鐵或巴士站,有大量人流,吸引客人行過時買杯解渴。
clip_image015
旺角弼街「椰汁大王」開業後,數鋪之隔便出現「十八子椰汁先生」。負責人李小姐表示生意極好,每日賺上七、八千元,但記者未見太多人幫襯,反而椰汁大王常大排長龍。黃志強為椰汁店日忙夜忙,每日由早上十一時做到午夜十二時。兩個仔仔惟有憑畫寄意,希望爸爸多點時間陪伴自己。
clip_image016
貪新鮮熱潮易過然而「椰汁大王」開業至今短短半年,除了要面對椰子漲價一成,令成本上漲的問題,還有兩大隱憂。第一,是人紅自然有人跟。在弼街總店附近,便有一間開業個多月的「十八子椰汁先生」,而水渠道分店旁的涼茶鋪,亦同樣突然兼賣椰汁,並自封老字號。「十八子椰汁先生」的負責人李小姐,斷然否認「跟風」,「我clip_image017一家三十幾年前就clip_image018深水clip_image002[2]推木頭車賣椰汁clip_image005[1]!我而家只係重操故業clip_image019!」
記者試過三間店鋪的椰汁,發現「椰汁大王」最好味,其餘兩間則太甜,明顯冰糖落得太重手,而幫襯的亦只是小貓三數隻;故黃志強千叮萬囑記者,不要將椰肉、椰水及花奶的比例公開,以免被人偷橋。
隱憂之二,是clip_image020仔客都是貪新鮮,恐怕熱潮易過。黃志強的椰子供應商甄想記負責人陳伯濠,便明言不太睇好。「香港人都鍾意一窩蜂,過一排熱潮退卻就無咁多人幫襯,我估計最多半年玩完。就算熱潮持續,佢clip_image017[1]每日已經要八百個椰子,我clip_image017[2]貨源已俾晒佢clip_image017[3],而香港亦只有數間椰子供應商,我怕佢clip_image017[4]就算擴充,亦無咁多椰子供應。」
不過,現時的黃志強對成績很是興奮,又點會聽入耳,他正四處物色鋪位,希望建立真正的「椰汁大王」王國。
clip_image021
不同產地不同用途
開業資料03/08租金(地鋪及倉庫)$180,000*
裝修$100,000
機器$30,000
雜費$6,000
材料$1,000
總投資$317,000
*兩個月按金、一個月上期營業資料7月營業額$740,000△
租金$220,000
人工$120,000^
材料$190,000
雜費$24,000
盈利$186,000
△四間鋪
^八個全職員工撰文