正则表达式中的非获取匹配

正则表达式是代码中的常用技巧,主要用来匹配符合某种句法规则的字符串。常用的正则表达式本文不再赘述,详细可以参考wikiPythonJava)。本文主要讨论的是非获取匹配的正则表达式。

常见的非获取匹配有以下几种:

(?:pattern) 主要用来匹配pattern但不获取结果。我们看下面的例子。

>>> re.match("win (7|vista)", "win 7").group()
'win 7'

>>> re.match("win (7|vista)", "win 7").groups()
('7',)

>>> re.match("win (?:7|vista)", "win 7").group()
'win 7'

>>> re.match("win (?:7|vista)", "win 7").groups()
()

可以看出,使用这种模式括号中匹配内容并没有存储供以后使用。它是比“win 7|win vista”更加简便的形式。

以上是Python的示例,Java代码如下(以后的例子不再赘述):

Pattern ptn = Pattern.compile("win (7|vista)");
Matcher m = ptn.matcher("win 7");
while (m.find()) {
    for (int i = 0; i <= m.groupCount(); i++) {
        System.out.println(m.group(i));
    }
}

(?=pattern) 正向肯定预查。它在任何匹配pattern的字符串处查找字符串,它仅仅是检查是否匹配,而不将预查的结果包含其中。当然,它也是非获取匹配。看个例子就明白了。

>>> re.match("win (?=7|vista)", "win 7").group()
'win '

>>> re.match("win (?=7|vista)", "win 7").groups()
()

(?!pattern) 正向否定预查。例子如下:

>>> re.match("win (?!7|vista)", "win 8").group()
'win '

>>> re.match("win (?!7|vista)", "win 8").groups()
()

(?<=pattern) 反向肯定预查。查找某个字串之前是否满足pattern。

>>> re.search("(?<=95|98)win", "98win").group()
'win'

>>> re.search("(?<=95|98)win", "98win").groups()
()

(?<!pattern) 反向否定预查。

>>> re.search("(?<!95|98)win", "7win").group() 
'win'

>>> re.search("(?<!95|98)win", "7win").groups()
()

现在我们来看实际中的需求。现在我有一个字符串,这个字符串是由中文和英文组成,现在我要把这个字符串切成一个个只由中文或者英文组成的字串。比如说将“McCulloch与Pitts将神经系统”切成”McCulloch“、“与”、“ Pitts“、”将神经系统“。

首先要知道匹配中文的正则是”[\u4e00-\u9fa5]“,在Python中,我们可以使用findall方法:

reg = re.compile(u"[\u4e00-\u9fa5]+|[A-Za-z]+")
print "\t".join([w for w in reg.findall(u"McCulloch与Pitts将神经系统")])

对于Java,除了使用这种方式匹配,我们还使用刚刚说过的非获取匹配来达到同样的效果。对于需求的句子,可以看到要切分的地方,要么是中文接英文,要么是英文接中文。所以我们在反向肯定预查到中文然后正向肯定预查到英文处调用split操作,或者在反向肯定预查到英文正向肯定预查到中文处调用同样的操作。于是有:

Pattern ptn = Pattern.compile(
    "(?<=[\u4e00-\u9fa5])(?=[A-Za-z])|(?<=[A-Za-z])(?=[\u4e00-\u9fa5])", 
    Pattern.UNICODE_CASE);
String[] words = ptn.split("McCulloch与Pitts将神经系统");
for(String word: words) {
    System.out.print(word + "\t");
}

可以看到达到了一样的效果,但是,在我的测试中,此法对Python中的正则库中的split方法并不适用。所以我们解决问题确实需要灵活多变的策略。

最后,我们再讨论以下js的实现。对于js来说,由于js不支持反向预查。所以,用和Python方法类似的简单策略。我们使用加上“g“修饰符的match方法(表示全局匹配,而不是只匹配一次)。

var testStr = "McCulloch与Pitts将神经系统";
var regExp = /[\u4e00-\u9fa5]+|[A-Za-z]+/g;

testStr.match(regExp).forEach(function(i){
    document.write(i+"  ");
});

Browser和Server持续同步的几种方式(jQuery+tornado演示)

在B/S模型的Web应用中,客户端常常需要保持和服务器的持续更新。这种对及时性要求比较高的应用比如:股票价格的查询,实时的商品价格,自动更新的twitter timeline以及基于浏览器的聊天系统(如GTalk)等等。由于近些年AJAX技术的兴起,也出现了多种实现方式。本文将对这几种方式进行说明,并用jQuery+tornado进行演示,需要说明的是,如果对tornado不了解也没有任何问题,由于tornado的代码非常清晰且易懂,选择tornado是因为其是一个非阻塞的(Non-blocking IO)异步框架(本文使用2.0版本)。

在开始之前,为了让大家有个清晰的认识,首先列出本文所要讲到的内容大概。本文将会分以下几部分:

  1. 普通的轮询(Polling)
  2. Comet:基于服务器长连接的“服务器推”技术。这其中又分为两种:
    1. 基于AJAX和基于IFrame的流(streaming)方式
    2. 基于AJAX的长轮询(long-polling)方式
  3. WebSocket

PIL应用之生成验证码图片

现在的网页中,为了防止机器人提交表单,图片验证码是很常见的应对手段之一。这里就不详细介绍了,相信大家都遇到过。

现在就给出用Python的PIL库实现验证码图片的代码。代码中有详细注释。

#!/usr/bin/env python
#coding=utf-8

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="ae_AlArabiya.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance = 2):
    '''
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串 
    '''

    width, height = size # 宽, 高
    img = Image.new(mode, size, bg_color) # 创建图形
    draw = ImageDraw.Draw(img) # 创建画笔

    def get_chars():
        '''生成给定长度的字符串,返回列表格式'''
        return random.sample(chars, length)

    def create_lines():
        '''绘制干扰线'''
        line_num = random.randint(*n_line) # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            #结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        '''绘制干扰点'''
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
        
        for w in xrange(width):
            for h in xrange(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        '''绘制验证码字符'''
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开
        
        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                    strs, font=font, fill=fg_color)
        
        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)

    return img, strs

if __name__ == "__main__":
    code_img = create_validate_code()
    code_img.save("validate.gif", "GIF")

最后结果返回一个元组,第一个返回值是Image类的实例,第二个参数是图片中的字符串(比较是否正确的作用)。

windows下安装opencv

opencv(open source computer vision library)是一个基于C/C++语言的开源图像函数处理库。

它的主要模块包括:

  • cv —— 核心函数库
  • cvaux —— 辅助函数库
  • cxcore —— 数据结构与线性代数库
  • highgui —— GUI函数库
  • ml —— 机器学习函数库

opencv是跨平台的,支持包括windows、unix以及android等平台。下图是英文wiki上一张关于opencv概述的图。关于opencv的一系列功能以及特性,本文不再赘述,如果要了解,请移步至opencv的中文站点介绍

opencv overview

本文将会讲解在wndows上如何使用VS2010安装opencv,并以一个简单的程序作为结束。opencv在近期推出了2.3版本,不过本文仍然以2.2版本为例。其他安装请参考中文安装页面

用PIL实现滤镜

本来想写一个系列,已经有了第一篇《用PIL实现滤镜(一)——素描、铅笔画效果》。最后关于各种滤镜的计算方法,分系列写实在是乏善可陈。

目前,由于Python语言限制,有些算法的效率十分低下(比如油画——oil painting)。在完成全部预计的效果之后,会再考虑优化效率。其实做这个东西完全是学习之用,估计也没有什么太大的实用价值:)

现在,代码已经托管在bitbucket上,如果你感兴趣,可以查看wiki。要获得代码,运行命令:

$ hg clone http://bitbucket.org/chineking/pil-filter-extension

代码中都有详细的注释,具体细节请参考代码。

关于作者

残阳似血(@秦续业),程序猿一枚,把梦想揣进口袋的挨踢工作者。现加入阿里云,研究僧毕业于上海交通大学软件学院ADC实验室。熟悉分布式数据分析(DataFrame并行化框架)、基于图模型的分布式数据库和并行计算、Dpark/Spark以及Python web开发(Django、tornado)等。

博客分类

点击排行

标签云

扫描访问

主题

残阳似血的微博