python3去除emoji表情符号

最近项目中使用load file导入文件到低版本MySQL中遇到卡住问题,最终问题为文本文件中某些行包含了emoji表情符号。所以使用python去掉这个emoji符号,然后再导出数据库。

需要安装如下模块:

https://pypi.org/project/demoji/

安装完demoji模块后需要手动下载emoji json文件,如果服务器无法上网,可以本地电脑运行,然后提取这个codes.json文件上传到服务器对应的目录。

>>> import demoji
>>> demoji.download_codes()
Downloading emoji data ...
... OK (Got response in 0.14 seconds)
Writing emoji data to /Users/brad/.demoji/codes.json ...
... OK

代码如下:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

import os
import demoji

file_path = "org_loadfile.txt"
final_fine = "loadfile.txt"

# 如果最终文件存在,则删除
if os.path.exists(final_fine):
    os.remove(final_fine)

with open(file_path, 'r') as file:
    for line in file:
        # print(demoji.replace(line, "__"))
        # 替换表情符号为 空
        rap_line = demoji.replace(line, "")
        # 写入文件
        with open(final_fine, 'a') as f:
            f.write(rap_line)


CentOS 6.10 yum 报PyUnicodeUCS4_AsUTF8String解决过程

近日朋友向我求助说是在CentOS6中编译安装了python2.7后面不知怎么搞的yum也用不了的,本以为很简单的事情,把yum里的头替换一下就好了,结果事情不是那个简单。

参考这篇文章重装 https://www.lizenghai.com/archives/17330.html,所使用的包如下:

libxml2-python-2.7.6-21.el6_8.1.x86_64.rpm
python-2.6.6-66.el6_8.x86_64.rpm
python-backports-1.0-5.el6.x86_64.rpm
python-backports-ssl_match_hostname-3.4.0.2-5.el6.noarch.rpm
python-chardet-2.2.1-1.el6.noarch.rpm
python-devel-2.6.6-66.el6_8.x86_64.rpm
python-iniparse-0.3.1-2.1.el6.noarch.rpm
python-libs-2.6.6-66.el6_8.x86_64.rpm
python-pycurl-7.19.0-9.el6.x86_64.rpm
python-setuptools-0.6.10-4.el6_9.noarch.rpm
python-urlgrabber-3.9.1-11.el6.noarch.rpm
rpm-build-4.8.0-59.el6.x86_64.rpm
rpm-libs-4.8.0-59.el6.x86_64.rpm
rpm-python-4.8.0-59.el6.x86_64.rpm
yum-3.2.29-81.el6.centos.noarch.rpm
yum-metadata-parser-1.1.2-16.el6.x86_64.rpm
yum-plugin-aliases-1.1.30-41.el6.noarch.rpm
yum-plugin-fastestmirror-1.1.30-41.el6.noarch.rpm
yum-utils-1.1.30-41.el6.noarch.rpm

下面这些是一些记录,看不懂可忽略这一段
rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps        ##强制清除已安装的程序及其关联
 whereis python |xargs rm -frv ##删除所有残余文件 ##xargs,允许你对输出执行其他某些命令
 whereis python ##验证删除,返回无结果说明清除干净
 删除现有的yum
 [root@localhost rx]# rpm -qa|grep yum
 yum-metadata-parser-1.1.2-16.el6.x86_64
 yum-plugin-aliases-1.1.30-41.el6.noarch
 yum-plugin-fastestmirror-1.1.30-41.el6.noarch
 yum-plugin-protectbase-1.1.30-41.el6.noarch
 yum-3.2.29-81.el6.centos.noarch
 yum-utils-1.1.30-41.el6.noarch
 rpm -qa|grep yum|xargs rpm -ev --allmatches --nodeps    
 创建一个目录:python-need-pkg,将这些包下载下来,我是用centos6.10  DVD1 ios 镜像中提取的包。下载响应的包(注意:根据自己的系统下载,源中版本会更新,具体查看URL中的版本再下载!)
 5.执行命令安装
 1>执行这一步的时候没有依赖问题
 rpm -Uvh --replacepkgs python*.rpm
 行这一步的时候报依赖问题,有两个,
 rpm -Uvh --replacepkgs rpm-python.rpm yum.rpm
 error: Failed dependencies:
         libxml2-python is needed by yum-utils-1.1.30-41.el6.noarch
 rpm -ivh --force yum-utils-1.1.30-41.el6.noarch.rpm
 find /usr/lib64/  -type f -name "*.so" | xargs nm -AD | grep PyUnicodeUCS4_AsUTF8String 
 /usr/lib64/python2.6/lib-dynload/_sqlite3.so:                 U PyUnicodeUCS4_AsUTF8String
 /usr/lib64/python2.6/lib-dynload/cPickle.so:                 U PyUnicodeUCS4_AsUTF8String
 /usr/lib64/python2.6/site-packages/rpm/_rpmmodule.so:                 U PyUnicodeUCS4_AsUTF8String
 rpm -qf /usr/lib64/python2.6/lib-dynload/_sqlite3.so
 python-libs-2.6.6-66.el6_8.x86_64
 rpm -qf /usr/lib64/python2.6/lib-dynload/cPickle.so
 python-libs-2.6.6-66.el6_8.x86_64
 rpm -qf /usr/lib64/python2.6/site-packages/rpm/_rpmmodule.so
 rpm-python-4.8.0-59.el6.x86_64
 nm -AD /lib/libpython2.6.so.1.0 | grep PyUnicodeUCS4_AsUTF8String 
 libpython2.6.so.1.0 => /usr/lib64/libpython2.6.so.1.0 (0x00007f139db99000)
 libpython2.6.so.1.0 => /lib/libpython2.6.so.1.0 (0x00007f6be9933000)
 libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007f139cb90000)
 libnssutil3.so => /usr/lib/libnssutil3.so (0x00007f6be892a000)

后面重装了系统自己带的python2.6.6及yum也还是报 /usr/lib64/python2.6/site-packages/rpm/_rpmmodule.so undefined symbol:PyUnicodeUCS4_AsUTF8String

后面自己在虚拟机中安装了一个干净的CentOS6.10对比才发现问题所在。下面这个是干净系统的 ldd

[root@localhost ~]# ldd /usr/lib64/python2.6/site-packages/rpm/_rpmmodule.so
         linux-vdso.so.1 =>  (0x00007fff9c3e9000)
         librpm.so.1 => /usr/lib64/librpm.so.1 (0x00007f6a00ddf000)
         libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f6a00bc0000)
         libcap.so.2 => /lib64/libcap.so.2 (0x00007f6a009bb000)
         libacl.so.1 => /lib64/libacl.so.1 (0x00007f6a007b3000)
         libdb-4.7.so => /lib64/libdb-4.7.so (0x00007f6a0043e000)
         librpmio.so.1 => /usr/lib64/librpmio.so.1 (0x00007f6a0020e000)
         libnss3.so => /usr/lib64/libnss3.so (0x00007f69ffec6000)
         libbz2.so.1 => /lib64/libbz2.so.1 (0x00007f69ffcb5000)
         libz.so.1 => /lib64/libz.so.1 (0x00007f69ffa9e000)
         libelf.so.1 => /usr/lib64/libelf.so.1 (0x00007f69ff888000)
         libpopt.so.0 => /lib64/libpopt.so.0 (0x00007f69ff67f000)
         liblzma.so.0 => /usr/lib64/liblzma.so.0 (0x00007f69ff45d000)
         liblua-5.1.so => /usr/lib64/liblua-5.1.so (0x00007f69ff230000)
         libm.so.6 => /lib64/libm.so.6 (0x00007f69fefac000)
         libpython2.6.so.1.0 => /usr/lib64/libpython2.6.so.1.0 (0x00007f69fec05000)
         librt.so.1 => /lib64/librt.so.1 (0x00007f69fe9fd000)
         libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f69fe7e0000)
         libc.so.6 => /lib64/libc.so.6 (0x00007f69fe44b000)
         libdl.so.2 => /lib64/libdl.so.2 (0x00007f69fe247000)
         /lib64/ld-linux-x86-64.so.2 (0x00005617d7672000)
         libattr.so.1 => /lib64/libattr.so.1 (0x00007f69fe042000)
         libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f69fde2b000)
         libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007f69fdbfc000)
         libplc4.so => /lib64/libplc4.so (0x00007f69fd9f7000)
         libplds4.so => /lib64/libplds4.so (0x00007f69fd7f2000)
         libnspr4.so => /lib64/libnspr4.so (0x00007f69fd5b3000)
         libutil.so.1 => /lib64/libutil.so.1 (0x00007f69fd3af000)

对比发现这两处不一样:

libpython2.6.so.1.0 => /usr/lib64/libpython2.6.so.1.0 (0x00007f139db99000)
libpython2.6.so.1.0 => /lib/libpython2.6.so.1.0 (0x00007f6be9933000)
libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007f139cb90000)
libnssutil3.so => /usr/lib/libnssutil3.so (0x00007f6be892a000)

解决它:

cd /etc/ld.so.conf.d/
echo “/usr/lib64/” > libnssutil3.conf
echo “/usr/lib64/” > libpython2.6.conf
echo “/lib64/” > libdb-4.7.conf

又遇到 无法 import time 的问题,解决方法:

vim /etc/profile.d/python.sh export PYTHONPATH=$PYTHONPATH:/usr/lib64/python2.6:/usr/lib64/python2.6/lib-dynload/

haprox external-check 健康检测

haprox external-check 健康检测可以自定义脚本来检测后端服务,这非常有利于那些经常出现假死,无法通过常规判断端口来检测后台是否正常的问题。
比如,RabbitMQ,业务程序自定义检查等。

HAProxy可以使用二进制或脚本运行外部命令来执行运行状况检查。 当这样做的时候,它需要将自己fork一个新进程。
如果您使用chroot,请确保该命令及其所有依赖项在chroot中可用。
以下指令可用:

In the global section:
external-check: Allows the use of the external check feature. It is disabled by default for security purposes.

In the defaults, backend, or listen section:

option external-check: Enables the use of an external command to perform health check
external-check command : Name of the command to run.
下面的参数被传递给命令:

写了一个bash脚本测试,发现在backend那里传了4个参数,如下,只有后面两个可用
Quoted Values: NOT_USED NOT_USED 172.20.2.71 5672
Total Number of Parameters : 4
继续阅读“haprox external-check 健康检测”

python requests post date日期类型后端无法接收问题

说话一个GM后台开服信息需要手动在GM后台提交,这个手动做了3个月,终于忍不了重复劳动,手动输入造成漏填及填错的问题。亮出 Python 中的 requests 模块,听说用这个写爬虫不错,palapala

很快脚本可以从文件读取相关配置信息,并能登陆后台获取 session,并提交上数据。但问题出现了,其中有个开服日期(2016-03-08 14:00:00 这种 “yyyy-MM-dd HH:mm:ss”格式)没有保存到数据库中,期间使用各种工具抓包对比浏览器手动提交的post参数均没发现问题,用 Postman 插件提交相同的参数是正常的。询问过php开发、java开发、安卓开发,都觉得拼接的 post 参数没问题。那问题在哪呢?查看后台服务日志,发现有报错,但是日志不完整,只提交有错误,无法得知更多细节问题。

询问后台开发人员,得知后台框架是使用 java 中的 struts2,放 Google 一搜索 “struts2 date 问题”,好家伙果然有很多人进了这个坑。 比如这位贴吧网友 http://tieba.baidu.com/p/2782007443。通过这个网友的日志 http://zengyi.me/blog/2015/02/07/struts-string-to-date-bug/ 得知如下

问题原因在于,Struts2 会对七种时间格式进行自动转换,其中还硬编码了”yyyy-MM-dd’T’HH:mm:ss” 这么一种格式,这是美国语言中使用的日期格式。我的请求参数中,有个日期参数是 yyyy-MM-dd 格式的,而对于yyyy-MM-dd这种日期类型,在英语语言中是没法匹配的,由于Struts2匹配日期时,使用了Locale,因此,如果客户端默认的语言环境是英语,则 Struts 无法匹配需转换的日期格式,自动转换就失败了。用户的机器和我的模拟器的语言环境都是英语,于是日期参数没有转换成功,请求就失败了。

然后我做了个试验,正能正常提交数据的 Firefox 浏览器语言改成只留下 en-us\ en两个,重新提交页面,哈哈哈,日期一样没提交上去,所以 python 脚本里只要加上 headers = {"Accept-Language": "zh-CN,zh;q=0.8"}就能正常提交日期了。看来还是后台没有完善对多语言环境的支持,不过我在这个排查过程也疏忽了一些问题,比如,只关注了post参数正不正确,而没有好好对比 header 请求头里的相关信息,槽兄在帮我排查这个问题的时候提出了要对比下请求头,但唯独漏了 Accept-Language。

排查中所使用到的工具:
1、Fiddler Web Debugger
2、Postman
3、Wireshark
4、Firebug
5、Chrome Developer Tools
及 PyCharm IDE工具 Debug代码

参考文章:

http://polaris.blog.51cto.com/1146394/315403/
http://zengyi.me/blog/2015/02/07/struts-string-to-date-bug/
http://www.jiancool.com/article/80023377219/
http://docs.python-requests.org/zh_CN/latest/user/advanced.html

learning tornado 101

在 Ubuntu 14.04.1 LTS 桌面系统里安装了 pycharm-4.5.4,Linux 下的 pycharm 是通过 java 来运行的,所以需要安装 Java jdk.
安装命令很简单

sudo apt-get install openjdk-7-jdk

Tornado  FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。

Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅 C10K problem。)

请参见 Tornado 文档  Tornado 原文文档(镜像)以详细了解该 Web 框架。

Tornado 官网:http://www.tornadoweb.org/en/stable/

Tornado Github

下面是安装的最新版 Tornado 4.2.1,本应该下载 .tar.gz 包来手动安装,及其它的基础包的,方便以后服务器上安装统一的环境。
但是这次使用的 pip 安装,安装命令如下:

$ sudo pip install tornado
Downloading/unpacking tornado
  Downloading tornado-4.2.1.tar.gz (434kB): 434kB downloaded
  Running setup.py (path:/tmp/pip_build_root/tornado/setup.py) egg_info for package tornado
    
    no previously-included directories found matching 'docs/build'
    warning: no files found matching 'tornado/test/README'
Downloading/unpacking backports.ssl-match-hostname (from tornado)
  Downloading backports.ssl_match_hostname-3.4.0.2.tar.gz
  Running setup.py (path:/tmp/pip_build_root/backports.ssl-match-hostname/setup.py) egg_info for package backports.ssl-match-hostname
    
Downloading/unpacking certifi (from tornado)
  Downloading certifi-2015.9.6.2-py2.py3-none-any.whl (371kB): 371kB downloaded
Installing collected packages: tornado, backports.ssl-match-hostname, certifi
  Running setup.py install for tornado
    building 'tornado.speedups' extension
    x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c tornado/speedups.c -o build/temp.linux-x86_64-2.7/tornado/speedups.o
    tornado/speedups.c:2:20: fatal error: Python.h: 没有那个文件或目录
     #include <Python.h>
                        ^
    compilation terminated.
    /tmp/pip_build_root/tornado/setup.py:94: UserWarning:
    ********************************************************************
    WARNING: The tornado.speedups extension module could not
    be compiled. No C extensions are essential for Tornado to run,
    although they do result in significant speed improvements for
    websockets.
    The output above this warning shows how the compilation failed.
    
    Here are some hints for popular operating systems:
    
    If you are seeing this message on Linux you probably need to
    install GCC and/or the Python development package for your
    version of Python.
    
    Debian and Ubuntu users should issue the following command:
    
        $ sudo apt-get install build-essential python-dev
    
    RedHat, CentOS, and Fedora users should issue the following command:
    
        $ sudo yum install gcc python-devel
    
    If you are seeing this message on OSX please read the documentation
    here:
    
    http://api.mongodb.org/python/current/installation.html#osx
    ********************************************************************
    
      "The output above "
    command 'x86_64-linux-gnu-gcc' failed with exit status 1
    
    no previously-included directories found matching 'docs/build'
    warning: no files found matching 'tornado/test/README'
  Running setup.py install for backports.ssl-match-hostname
    
Successfully installed tornado backports.ssl-match-hostname certifi
Cleaning up...




$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import tornado
>>> tornado.version
'4.2.1'
>>> 

OK,开工!

About字符编码

打开”记事本”程序Notepad.exe,新建一个文本文件,内容就是一个”严”字,依次采用ANSI,Unicode,Unicode big endian 和 UTF-8编码方式保存。

然后,用文本编辑软件UltraEdit中的”十六进制功能”,观察该文件的内部编码方式。

1)ANSI:文件的编码就是两个字节”D1 CF”,这正是”严”的GB2312编码,这也暗示GB2312是采用大头方式存储的。

2)Unicode:编码是四个字节”FF FE 25 4E”,其中”FF FE”表明是小头方式存储,真正的编码是4E25。

3)Unicode big endian:编码是四个字节”FE FF 4E 25″,其中”FE FF”表明是大头方式存储。

4)UTF-8:编码是六个字节”EF BB BF E4 B8 A5″,前三个字节”EF BB BF”表示这是UTF-8编码,后三个”E4B8A5″就是”严”的具体编码,它的存储顺序与编码顺序是一致的。

UTF-8编码的文件中,BOM占三个字节。如果用记事本把一个文本文件另存为UTF-8编码方式的话,用UE打开这个文件,切换到十六进制编辑状态就可以看到开头的FFFE了。这是个标识UTF-8编码文件的好办法,软件通过BOM来识别这个文件是否是UTF-8编码,很多软件还要求读入的文件必须带BOM。可是,还是有很多软件不能识别BOM。

PHP在设计时就没有考虑BOM的问题,也就是说他不会忽略UTF-8编码的文件开头BOM的那三个字符。

由于必须在在Bo-Blog的wiki看到,同样使用PHP的Bo-Blog也一样受到BOM的困扰。其中有提到另一个麻烦:“受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIE、SESSION实现的功能全部无效。”这个应该就是Wordpress后台出现空白页面的原因了,因为任何一个被执行的文件包含了BOM,这三个字符都将被送出,导致依赖cookies和session的功能失效。

解决的办法嘛,如果只包含英文字符(或者说ASCII编码内的字符),就把文件存成ASCII码方式吧。用UE等编辑器的话,点文件->转换->UTF-8转ASCII,或者在另存为里选择ASCII编码。如果是DOS格式的行尾符,可以用记事本打开,点另存为,选ASCII编码。如果包含中文字符的话,可以用UE的另存为功能,选择“UTF-8 无 BOM”即可。

当然在Python程序中处理的时候遇到这种情况改怎么处理呢?

方法一:直接将源文件的格式修改一下,将其保存为UTF-8即可,但是很多时候传过来的源文件是没法修改的,这样就行不通了。

方法二:替换,就是将其中的\xef\xbb\xbf替换为空就可以了。

VIM 十六进制和文本模式切换

使用vim打开文件后,使用命令

:%!xxd 使用十六进制显示;
:%!xxd -r 返回文本显示。

只有十六进制部分的修改才会被采用。右边可打印文本部分的修改忽略不计。

vim以二进制打开文件

vim -b binfile

在 Vim 中将文件保为无 BOM 的 UTF-8 格式

查看文件格式

通常我们需要先查看文件格式是否与预期的一样,再根据结果决定是否需要修改(当然你也可以在不知道原格式的情况下直接修改),下面分别给出了查看文件编码和是否带有 BOM 的命令。

# 查看文件编码。
set fenc?
# 查看是否带 BOM。
set bomb?

修改文件格式

# 设置为 UTF-8 编码。
set fenc=utf-8
# 设置为无 BOM,如需设置为带 BOM 则使用 "set bomb"。
set nobomb
# 加上BOM标记
set bomb

使用linux命令删除UTF-8编码中的BOM

shell> grep -r -I -l $'^\xEF\xBB\xBF' /path | xargs sed -i 's/^\xEF\xBB\xBF//;q'
or
shell> grep -r -I -l $'^\xEF\xBB\xBF' /path | xargs sed -i 's/^\xEF\xBB\xBF//g'
or
shell> tail -c +4 old_file > new_file

如果使用SVN提交代码的话,可以在pre-commit钩子里加上相关代码用以杜绝BOM。

#!/bin/bash

REPOS="$1"
TXN="$2"

SVNLOOK=/usr/bin/svnlook

for FILE in $($SVNLOOK changed -t "$TXN" "$REPOS" | awk '/^[AU]/ {print $NF}'); do
    if $SVNLOOK cat -t "$TXN" "$REPOS" "$FILE" | grep -q $'^\xEF\xBB\xBF'; then
        echo "Byte Order Mark be found in $FILE" 1>&2
        exit 1
    fi
done

不含 BOM 的 UTF-8 才是标准形式!
带 BOM 的 UTF-8 就是赤裸裸的流氓!!!

 

不同编码的BOM表示

编码 十六进制表示
UTF-8 EF BB BF
UTF-16-BE FE FF
UTF-16-LE FF FE
UTF-32-BE 00 00 FE FF
UTF-32-LE FF FE 00 00
UTF-7 2B 2F 76和以下的一个字节:[ 38 39 2B 2F ]
en:UTF-1 F7 64 4C
en:UTF-EBCIC DD 73 66 73
en:Standard Compression Scheme for Unicode 0E FE FF
en:BOCU-1 FB EE 28及可能跟随着FF
GB-18030 84 31 95 33

vim 将文件从dos格式转换到unix格式

:set fileformat=unix
:w

vim 将文件从unix格式转换到dos格式

:set fileformat=dos
:w

VIM 状态栏显示 文件格式带bom
Show fileencoding and bomb in the status line
http://vim.wikia.com/wiki/Show_fileencoding_and_bomb_in_the_status_line
比如 [latin1], [iso-8859-15], [utf-8,B], etc.

if has("statusline")
 set statusline=%<%f\ %h%m%r%=%{\"[\".(&fenc==\"\"?&enc:&fenc).((exists(\"+bomb\")\ &&\ &bomb)?\",B\":\"\").\"]\ \"}%k\ %-14.(%l,%c%V%)\ %P
endif

其实有用的就是这段:

%{\"[\".(&fenc==\"\"?&enc:&fenc).((exists(\"+bomb\")\ &&\ &bomb)?\",B\":\"\").\"]\ \"}

关于Python脚本开头coding-utf-8的作用

关于Python脚本开头 # -*- coding: utf-8 -*- 的作用

 

1 指定文件编码类型

#!/usr/bin/python

是用来说明脚本语言是python的是要用/usr/bin下面的程序(工具)python,这个解释器,来解释python脚本,来运行python脚本的。
推荐使用下面这个

#!/usr/bin/env python

2 # -*- coding: utf-8 -*-

是用来指定文件编码为utf-8的,详情可以参考:
PEP 0263 — Defining Python Source Code Encodings
在此,详细的(主要是翻译)解释一下,为何要加这个编码声明,以及如何添加编码声明:

2.1 使用文件编码声明以前所遇到的问题

Python 2.1中,想要输入Unicode字符,只能用基于Latin-1的”unicode-escape”的方式输入 -> 对于其他非Latin-1的国家和用户,想要输入Unicode字符,就显得很繁琐,不方便。

希望是:

编程人员,根据自己的喜好和需要,以任意编码方式输入字符串,都可以,这样才正常。

2.2 建议选用的方案

所以,才有人给Python官方建议,所以才有此PEP 0263。

此建议就是:

允许在Python文件中,通过文件开始处的,放在注释中的,字符串形式的,声明,声明自己的python文件,用何种编码。

由此,需要很多地方做相应的改动,尤其是Python文件的解析器,可以识别此种文件编码声明。

2.3 具体如何声明python文件编码?

上面已经说了,是,文件开始处的,放在注释中的,字符串形式的,声明。

那具体如何声明,以什么样的格式去声明呢?

其实就是,你之前就见过的,这种:

# -*- coding: utf-8 -*-

对此格式的详细解释是:

  1. 如果没有此文件编码类型的声明,则python默认以ASCII编码去处理
    如果你没声明编码,但是文件中又包含非ASCII编码的字符的话,python解析器去解析的python文件,自然就会报错了。
  2. 必须放在python文件的第一行或第二行
  3. 支持的格式,可以有三种:

带等于号的:

# coding=

最常见的,带冒号的(大多数编辑器都可以正确识别的):

#!/usr/bin/env python
# -*- coding:  -*-

vim的:

#!/usr/bin/env python
# vim: set fileencoding= :

更加精确的解释是符合正则表达式

"coding[:=]\s*([-\w.]+)"

很明显,如果你熟悉正则表达式,也就可以写出来,其他一些合法的编码声明,以utf-8为例,比如:

coding:         utf-8
coding=utf-8
coding=                  utf-8
encoding:utf-8
crifanEncoding=utf-8

为了照顾特殊的Windows中的带BOM(’\xef\xbb\xbf’)的UTF-8
如果你的python文件本身编码是带BOM的UTF-8,即文件前三个字节是:’\xef\xbb\xbf’,那么:
即使你没有声明文件编码,也自动当做是UTF-8的编码
如果你声明了文件编码,则必须是声明了(和你文件编码本身相一致的)UTF-8
否则(由于声明的编码和实际编码不一致,自然)会报错

2.4 文件编码声明的各种例子

针对上面的规则,下面给出各种,合法的,非法的,例子,供参考:

一、合法的python文件编码声明

1、带声明了解释器的、Emacs风格的、(注释中的)文件编码声明

例子1:

#!/usr/bin/env python
# -*- coding: latin-1 -*-
import os, sys
...

例子2:

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
import os, sys
...

例子3:

#!/usr/bin/env python
# -*- coding: ascii -*-
import os, sys
...

2、不带声明了解释器的,直接用纯文本形式的

# This Python file uses the following encoding: utf-8
 import os, sys
 ...

3、文本编辑器也可以有多种(其他的)定义编码的方式

 #!/usr/bin/env python
 # coding: latin-1
 import os, sys
 ...
 

很明显,其中的没用-*-,直接用了coding加上编码值

4、不带编码声明的,默认当做ASCII处理

#!/usr/bin/env python
import os, sys
...

二、非法的python文件编码声明举例

1、少了coding:前缀

#!/usr/local/bin/python
# latin-1
import os, sys
...

2、编码声明不在第一行或第二行

#!/usr/bin/env python
#
# -*- coding: latin-1 -*-
import os, sys
...

3、不支持的,非法的字符编码(字符串)声明

#!/usr/bin/env python
# -*- coding: utf-42 -*-
import os, sys
...

2.5 python文件编码声明所遵循的理念

1.单个的完整的python源码文件中,只用单一的编码。

->

不允许嵌入了多种的编码的数据

否则会导致(python解释器去解析你的python文件时)报编码错误。

不太懂这段:

Any encoding which allows processing the first two lines in the way indicated above is allowed as source code encoding, this includes ASCII compatible encodings as well as certain multi-byte encodings such as Shift_JIS. It does not include encodings which use two or more bytes for all characters like e.g. UTF-16. The reason for this is to keep the encoding detection algorithm in the tokenizer simple.

2.这段也不太懂:

Handling of escape sequences should continue to work as it does now, but with all possible source code encodings, that is standard string literals (both 8-bit and Unicode) are subject to escape sequence expansion while raw string literals only expand a very small subset of escape sequences.

3.Python的分词器+编译器,会按照如下的逻辑去工作:

读取文件
不同的文件,根据其声明的编码去解析为Unicode
转换为UTF-8字符串
针对UTF-8字符串,去分词
编译之,创建Unicode对象
要注意的是:

Python中的标识符,都是ASCII的。

其余的内容,不翻译了。

至此,已经解释的够清楚了。
转载地址:http://www.crifan.com/python_head_meaning_for_usr_bin_python_coding_utf-8/

Django APPEND_SLASH 参数自动给网址结尾加’/’

Django seetings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 作用就是自动在网址结尾加’/’。

如下URL视图:

urls.py:
--------
from django.conf.urls.defaults import *
from mysite.views import hello

urlpatterns = patterns('',
('^hello/$', hello),
)

因定义了hello/结尾:

http://example.com/hello
自动转到
http://example.com/hello/

默认地,任何不匹配或尾部没有斜杠(/)的申请URL,将被重定向至尾部包含斜杠的相同字眼的URL。

当 seetings.py 设置为 APPEND_SLASH = False 时,访问 http://example.com/hello 将会返回 404。

所以我觉得还是按照官方默认开启这个参数方便些,如果不想要后面的 / ,可以在 urls.py 定义成 ‘^hello$’ 这样的形式。

python中的is、==和cmp()比较字符串

python 中的is、==和cmp(),比较字符串

经常写 shell 脚本知道,字符串判断可以用 =,!= 数字的判断是 -eq,-ne 等,但是 Python 确不是这样子地。
所以作为慢慢要转换到用 Python 写脚本,这些基本的东西必须要掌握到骨子里!

在 Python 中比较字符串最好是使用简单逻辑操作符。
例如,确定一个字符串是否和另外一个字符串匹配。正确的,你可以使用 is equal 或 == 操作符。你也可以使用例如 >= 或 < 来确定几个字符串的排列顺序。

从官方文档上看

The operators ``is`` and ``is not`` test for object identity: ``x is
y`` is true if and only if *x* and *y* are the same object.  ``x is
not y`` yields the inverse truth value.

cmp(...)
    cmp(x, y) -> integer

    Return negative if x<y, zero if x==y, positive if x>y.

也就是说 is 用来判断是否是同一个对象,is 是种很特殊的语法,你在其它的语言应该不会见到这样的用法。
python is 主要是判断 2 个变量是否引用的是同一个对象,如果是的话,则返回 true,否则返回 false。
判断数字相等不要用 is 操作符
http://onlypython.group.iteye.com/group/wiki/852-%E6%93%8D%E4%BD%9C%E7%AC%A6is%E7%9A%841%E4%B8%AA%E8%AF%A1%E5%BC%82%E9%97%AE%E9%A2%98

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

为什么两次 is 返回的是不同结果?不是应该都是 true 吗?
因为 string pooling (或叫intern)。 is 相等代表两个对象的 id 相同(从底层来看的话,可以看作引用同一块内存区域)。 至于为什么 “ABC” 被 intern 了而 “a bc” 没有,这是 Python 解析器实现决定的,可能会变。

== 用来判断两个对象的值是否相等(跟 Java 不同,Java 中 == 用来判断是否是同一个对象)。
今天我用 == 来判断两个 IP 地址 字符串是否相同。

#!/usr/bin/python

strtmp = '192.169.1.161'
file_object = open(r'public_ip.txt')
try:
    all_the_text = file_object.readlines()
    firstline = all_the_text[0].rstrip()
finally:
    file_object.close()

#print firstline

#if strtmp == firstline:
s = (strtmp is firstline)
print s
if (strtmp is firstline):
    print 'yes'
else:
    print 'no'

来个简单点的例子:

#-*-conding:utf-8-*-
i='xinwen';
m=input();
if i==m:
    print('yes');
else:
    print('no');

input();

在 if 判断语句中非常有用呐!

#!/usr/bin/python
# Filename: if.py

number = 23
guess = int(raw_input('Enter an integer : '))

if guess == number:
    print 'Congratulations, you guessed it.' # New block starts here
    print "(but you do not win any prizes!)" # New block ends here
elif guess < number:
    print 'No, it is a little higher than that' # Another block
    # You can do whatever you want in a block ...
else:
    print 'No, it is a little lower than that'
    # you must have guess > number to reach here

print 'Done'
# This last statement is always executed, after the if statement is executed

cmp() 函数则是相当于 <,==,> 但是在 Python3 中,cmp() 函数被移除了,所以我以后还是避免少用这个函数。

>>> x='a'
>>> x+'b' is 'ab'
False
>>> x+'b' == 'ab'
True
>>> cmp(x+'b','ab')
0
>>> id(x+'b')
32468384L
>>> id('ab')
46933696L
>>> 

注意:

>>> a='abc'
>>> b='abc'
>>> a is b
True
>>> id(a) == id(b)
True
>>> 

可以看出内容相同的字符串实际上是同一个对象(Java 中直接赋值的字符串也可用 == 来判断,但是使用 new 实例化的对象则需要使用equals(String s) 来判断)。

Python正则表达式指南

1. 正则表达式基础

1.1. 简单介绍

正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。

下图展示了使用正则表达式进行匹配的流程:

re_simple1

正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。

下图列出了Python支持的正则表达式元字符和语法:

pyre

1.2. 数量词的贪婪模式与非贪婪模式

正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式”ab*”如果用于查找”abbbc”,将找到”abbb”。而如果使用非贪婪的数量词”ab*?”,将找到”a”。

1.3. 反斜杠的困扰
与大多数编程语言相同,正则表达式里使用”\”作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”\”,那么使用编程语言表示的正则表达式里将需要4个反斜杠”\\\\”:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r”\\”表示。同样,匹配一个数字的”\\d”可以写成r”\d”。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。

1.4. 匹配模式
正则表达式提供了一些可用的匹配模式,比如忽略大小写、多行匹配等,这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍。

2.1.开始使用re

Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。

re.compile(strPattern[, flag]):

这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile(‘pattern’, re.I | re.M)与re.compile(‘(?im)pattern’)是等价的。
可选值有:

re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
M(MULTILINE): 多行模式,改变’^’和’$’的行为(参见上图)
S(DOTALL): 点任意匹配模式,改变’.’的行为
L(LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
U(UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:

a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")

re提供了众多模块方法用于完成正则表达式的功能。这些方法可以使用Pattern实例的相应方法替代,唯一的好处是少写一行re.compile()代码,但同时也无法复用编译后的Pattern对象。这些方法将在Pattern类的实例方法部分一起介绍。如上面这个例子可以简写为:

m = re.match(r'hello', 'hello world!')
print m.group()

re模块还提供了一个方法escape(string),用于将string中的正则表达式元字符如*/+/?等之前加上转义符再返回,在需要大量匹配元字符时有那么一点用。

2.2. Match

Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。

属性:

  1. string: 匹配时使用的文本。
  2. re: 匹配时使用的Pattern对象。
  3. pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
  4. endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
  5. lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
  6. lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。

方法:

  1. group([group1, …]):
    获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。
  2. groups([default]):
    以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。
  3. groupdict([default]):
    返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。
  4. start([group]):
    返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。
  5. end([group]):
    返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。
  6. span([group]):
    返回(start(group), end(group))。
  7. expand(template):
    将匹配到的分组代入template中然后返回。template中可以使用\id或\g、\g引用分组,但不能使用编号0。\id与\g是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符’0’,只能使用\g0。
import re
m = re.match(r'(\w+) (\w+)(?P.*)', 'hello world!')

print "m.string:", m.string
print "m.re:", m.re
print "m.pos:", m.pos
print "m.endpos:", m.endpos
print "m.lastindex:", m.lastindex
print "m.lastgroup:", m.lastgroup

print "m.group(1,2):", m.group(1, 2)
print "m.groups():", m.groups()
print "m.groupdict():", m.groupdict()
print "m.start(2):", m.start(2)
print "m.end(2):", m.end(2)
print "m.span(2):", m.span(2)
print r"m.expand(r'\2 \1\3'):", m.expand(r'\2 \1\3')

### output ###
# m.string: hello world!
# m.re: <_sre.SRE_Pattern object at 0x016E1A38>
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): ('hello', 'world')
# m.groups(): ('hello', 'world', '!')
# m.groupdict(): {'sign': '!'}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r'\2 \1\3'): world hello!

2.3. Pattern

Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。

Pattern不能直接实例化,必须使用re.compile()进行构造。

Pattern提供了几个可读属性用于获取表达式的相关信息:

  1. pattern: 编译时用的表达式字符串。
  2. flags: 编译时用的匹配模式。数字形式。
  3. groups: 表达式中分组的数量。
  4. groupindex: 以表达式中有别名的组的别名为键、以该组对应的编号为值的字典,没有别名的组不包含在内。
import re
p = re.compile(r'(\w+) (\w+)(?P.*)', re.DOTALL)

print "p.pattern:", p.pattern
print "p.flags:", p.flags
print "p.groups:", p.groups
print "p.groupindex:", p.groupindex

### output ###
# p.pattern: (\w+) (\w+)(?P.*)
# p.flags: 16
# p.groups: 3
# p.groupindex: {'sign': 3}

实例方法[ | re模块方法]:

  1. match(string[, pos[, endpos]]) | re.match(pattern, string[, flags]):
    这个方法将从string的pos下标处起尝试匹配pattern;如果pattern结束时仍可匹配,则返回一个Match对象;如果匹配过程中pattern无法匹配,或者匹配未结束就已到达endpos,则返回None。
    pos和endpos的默认值分别为0和len(string);re.match()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。
    注意:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符’$’。
    示例参见2.1小节。
  2. search(string[, pos[, endpos]]) | re.search(pattern, string[, flags]):
    这个方法用于查找字符串中可以匹配成功的子串。从string的pos下标处起尝试匹配pattern,如果pattern结束时仍可匹配,则返回一个Match对象;若无法匹配,则将pos加1后重新尝试匹配;直到pos=endpos时仍无法匹配则返回None。
    pos和endpos的默认值分别为0和len(string));re.search()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。

    # encoding: UTF-8
    import re
    
    # 将正则表达式编译成Pattern对象
    pattern = re.compile(r'world')
    
    # 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
    # 这个例子中使用match()无法成功匹配
    match = pattern.search('hello world!')
    
    if match:
        # 使用Match获得分组信息
        print match.group()
    
    ### 输出 ###
    # world
    
  3. split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]):
    按照能够匹配的子串将string分割后返回列表。maxsplit用于指定最大分割次数,不指定将全部分割。

    import re
    
    p = re.compile(r'\d+')
    print p.split('one1two2three3four4')
    
    ### output ###
    # ['one', 'two', 'three', 'four', '']
    
  4. findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags]):
    搜索string,以列表形式返回全部能匹配的子串。

    import re
    
    p = re.compile(r'\d+')
    print p.findall('one1two2three3four4')
    
    ### output ###
    # ['1', '2', '3', '4']
    
  5. finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags]):
    搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。

    import re
    
    p = re.compile(r'\d+')
    for m in p.finditer('one1two2three3four4'):
        print m.group(),
    
    ### output ###
    # 1 2 3 4
    
  6. sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]):
    使用repl替换string中每一个匹配的子串后返回替换后的字符串。
    当repl是一个字符串时,可以使用\id或\g、\g引用分组,但不能使用编号0。
    当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
    count用于指定最多替换次数,不指定时全部替换。

    import re
    
    p = re.compile(r'(\w+) (\w+)')
    s = 'i say, hello world!'
    
    print p.sub(r'\2 \1', s)
    
    def func(m):
        return m.group(1).title() + ' ' + m.group(2).title()
    
    print p.sub(func, s)
    
    ### output ###
    # say i, world hello!
    # I Say, Hello World!
    
  7. subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]):
    返回 (sub(repl, string[, count]), 替换次数)。

    import re
    
    p = re.compile(r'(\w+) (\w+)')
    s = 'i say, hello world!'
    
    print p.subn(r'\2 \1', s)
    
    def func(m):
        return m.group(1).title() + ' ' + m.group(2).title()
    
    print p.subn(func, s)
    
    ### output ###
    # ('say i, world hello!', 2)
    # ('I Say, Hello World!', 2)
    

以上就是Python对于正则表达式的支持。熟练掌握正则表达式是每一个程序员必须具备的技能,这年头没有不与字符串打交道的程序了。笔者也处于初级阶段,与君共勉,^_^
另外,图中的特殊构造部分没有举出例子,用到这些的正则表达式是具有一定难度的。有兴趣可以思考一下,如何匹配不是以abc开头的单词,^_^

本文从这里转载的:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html

在这里感谢博主的排版,我虽然转载,也花了很长时间来排版。体会到了排版对于阅读的总要性,

我觉得浩哥和陈子的博客排版就非常适合用来参考了。

Tips:

  1. http://www.iteedu.com//plang/python/pyrediary/index.php
  2. http://www.regexlab.com/
  3. http://en.wikipedia.org/wiki/Regular_expression

分析一正则表达式替换例子

今天下班途中,在我的 Pocket 列表中看到前些天丢进来的《Python编程中常用的12种基础知识总》一文中的第一个例子:
正则表达式替换。
目标: 将字符串 line 中的 overview.gif 替换成其他字符串

随便分析了一下,感觉不通顺,虽想回家好好分析下,结果掉一坑里去了。文中例子代码丢失一段,导致我想了半天都没想去为啥就要这样匹配,后来机智的我想到肯定是代码不全,Google 了下找到了。在这里鄙视下那些 Ctrl C,Ctrl V 的,万一一个启动脚本里有个 rm -rf / tmp/xxx 那不完蛋了。

cat 03_subtest.py

#!/usr/bin/python

import re

# line = '<IMG ALIGN="middle" SRC=\'#\'" /span>    # 就是这段坑了我
line = '<IMG ALIGN="middle" SRC="overview.gif" BORDER="0" ALT="">'
# 将正则表达式编译成Pattern对象
pattern = re.compile(r'(?<=SRC=)"([\w+\.]+)"',re.I)

# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None 注意: 其中 \1 是上面()组匹配到的数据,可以通过这样的方式直接引用
str1 = pattern.sub(r'"\1****"',line)
print str1

str2 = pattern.sub(r'replace_str_\1',line)
print str2

str3 = pattern.sub(r'"testetstset"',line)
print str3

来分析上面的正则:

(?<=SRC=)"([\w+\.]+)"

拆分成两段来理解:1、 (?<=SRC=)     2、"([\w+\.]+)"

  1. (?<=SRC=)
    (?<=xxx) A positive lookbehind --反向预搜索啥是预搜索?反向应该就是反过来的意思吧?在正则中特殊符号:“^”,”$”,”\b”。它们都有一个共同点,那就是:它们本身不匹配任何字符,只是对 “字符串的两头” 或者 “字符之间的缝隙” 附加了一个条件。这个预搜索也是像它们一样是个“附加条件”,不过他们的表示方法更加灵活。(?<=xxx) 所在缝隙(就是要匹配的东西缝隙,这里就是"([\w+\.]+)" 匹配到的内容为分割线)的左侧,必须能够匹配上 xxx 这部分的表达式。因为它只是在此作为这个缝隙上附加的条件,所以它并不影响后边的表达式去真正匹配这个缝隙之后的字符。这就类似 “\b”,本身不匹配任何字符。”\b” 只是将所在缝隙之前、之后的字符取来进行了一下判断,不会影响后边的表达式来真正的匹配。

    这里的意思是要匹配的内容前面(左则)为 SRC= 才算。就好比”^”要匹配的内容必须是第一个位置一个道理。

  2. "([\w+\.]+)"
    one-of-2 one-of[\w+\.] 表示 任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_,. 中任意一个,
    这里面的+号我觉得是多余的了[\w+\.]+ 这里加上加号就是匹配至少1个里面的东西。"([\w+\.]+)" 加上() 表示一个组,然后再加上””就可以匹配出 “middle” “overview.gif” “0” 这三个结果了,怎么得到我们想要的”overview.gif”?

    就要用到前面的 “附加条件了” 附加条件就是 匹配前面有 SRC=

输出结果如下:

javasboy@debianp3  ~/python # python 03_subtest.py
<IMG ALIGN="middle" SRC="overview.gif****" BORDER="0" ALT="">
<IMG ALIGN="middle" SRC=replace_str_overview.gif BORDER="0" ALT="">
<IMG ALIGN="middle" SRC="testetstset" BORDER="0" ALT="">

Tips:

  1. 揭开正则表达式的神秘面纱
  2. RegExr:
    http://gskinner.com/RegExr/ 提供了一个方便的开发工具,可以很方便的测试正则表达式是否有效。

    1. 表达式单元拆分
    2. 内容实时匹配
    3. 多模式
  3. Debuggex:
    http://www.debuggex.com/ 提供更为丰富的正则执行流可视化。

下一篇将会转载一篇《Python正则表达式指南》

The Zen of Python

Python禅道 python-logo

在Python 中当你键入

import this

就会出现Python的禅道

The Zen of Python, by Tim Peters

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!

译文:

美丽优于丑陋。

清楚优于含糊。

简单优于复杂。

复杂优于繁琐。

平坦优于曲折。

宽松优于密集。

重要的是可读性。

特殊的案例不足以特殊到破坏规则。

尽管实践可以打破真理。

错误却不可置之不理。

除非另有明确要求。

面对模棱两可,拒绝猜测。

总会有一个 —— 最好是只有一个 —— 显而易见的方式来明辨。

哪怕这种方式在开始的时候可能并不明显。

现在有比没有好。

尽管没有经常好于现在。

如果如何实现很难被解释清楚,那么这个想法就是一个坏想法。

如果如何实现可以被很好的解释,那么这是一个好想法。

命名空间就是一个非常好的想法 —— 让我们在这方面多做些工作吧!

命名空间赞,吾人多实用。

ps: 三年前准备学习Perl的却把单词记错成Python,学了一会Python发现搞错了,然后又奋力学起Perl,并准备为此奋斗一生,然而现在又必须放下一切,挽起袖子重新拾起Python来干活。TMD,这次劳资必须啃下你,终身用Python。

人生苦短,我用 Python

…突然想念北京那群Perl的朋友了,希望得到你们的支持。