Python中的正则表达式
概述
在python中:编码:unicode-->str 解码:str-->Unicode
在python中,编码函数是encode(),解码函数是decode()。
【需要注意的一点是,如果我们调用str.encode(),这里涉及到一个隐士的类型转化,会现将str转化成unicode,才能进行编码,这也是不太容易理解的地方。所以,str.encode()实际上就等价于str.decode(sys.defaultencoding).encode().而sys.defaultencoding一般是ascii,它是不能用来编码中文字符的。】
各种编码方式
【Python中,不论是Python 2.x还是Python 3.x中,总体上说,字符都只有2大类:
一类是通用的Unicode字符;另一类是,(unicode被编码后的),某种编码类型的字符,比如UTF-8,GBK等等类型的字符】
ASCII(American Standard Code forInformation Interchange),是单字节编码,一个字节等于8位二进制数,所以单字节可以表示256个不同的字符,可以表示所有的英文字符和许多的控制符号。不过ASCII只用到了其中的一半(\x80以下)。
MBCS(Multi-Byte Character Set) ,这一类编码有很多种,其中就有GBK:为了表示其他语言的字符,采用双字节编码,并且兼容ASCII,采用的方式是:如果第一个字节是\x80以下,则仍然表示ASCII字符;而如果是\x80以上,则跟下一个字节一起(共两个字节)表示一个字符,然后跳过下一个字节,继续往下判断。
后来的后来·····人们为了方便统一了编码方式··unicode
Unicode的编码方式的标准用很多:【UCS-2(两个字节表示一个字符)】【UTF-8(变长的,并且兼容ASCII,ASCII字符使用1字节表示,其他符号用更多编码方式匹配)】
By the way : BOM(Byte Order Mark)是一种标记,就是在我们打开记事本的时候,最开始读入的是什么编码,我们就按这种编码方式打开整个文本,这就是为什么我们不用在打开记事本的时候先选择编码方式··
str是字节串,是它是unicode经过编码后的字节组成的序列。
例如unicode 中的“水”字,经过UTF-8编码后得到的str为“\xe6\xb0\xb4”len()==3
【Python中用中文字符串前面加上u表示这个字符串为unicode】
unicode是字符串,对str进过正确解码可以获得 ,对于(u“水”)len()==1
具体操作
"python 2.x中,普通的,用引号括起来的字符,就是str;此时字符串的编码类型,对应着你的Python文件本身保存为何种编码有关,最常见的Windows平台中,默认用的是GBK"
str->unicode: 需要解码的str.decode(解码方式【例如:‘UTF-8‘】)
unicode->str : 先声明一个Unicode字符unicodechar,例如【u‘水’】,unicodechar.encode(编码对应的方式【例如:‘UTF-8‘】)
by the way: 在python 3.x,具体操作有很大区别
字符码的声明:源代码文件中,如果有用到非ASCII字符,则需要在文件头部进行字符编码的声明,如下:
#-*- coding: UTF-8 -*-
【实际上Python只检查#、coding和编码字符串,其他的字符都是为了美观加上的。另外,Python中可用的字符编码有很多,并且还有许多别名,还不区分大小写,比如UTF-8可以写成u8。】
关于文件操作
内置的open()方法打开文件时,read()读取的是str
write()写入时,如果参数是unicode,则需要使用你希望写入的编码进行encode(),如果是其他编码格式的str,则需要先用该str的编码进行decode(),转成unicode后再使用写入的编码进行encode()。
如果直接将unicode作为参数传入write()方法,Python将先使用源代码文件声明的字符编码进行编码然后写入。
另外,模块codecs提供了一个open()方法,可以指定一个编码打开文件,使用这个方法打开的文件读取返回的将是unicode。写入时,如果参数是unicode,则使用open()时指定的编码进行编码后写入;如果是str,则先根据源代码文件声明的字符编码,解码成unicode后再进行前述操作。相对内置的open()来说,这个方法比较不容易在编码上出现问题。
Tips:
1. 使用字符编码声明,并且同一工程中的所有源代码文件使用相同的字符编码声明
2. 抛弃str,全部使用unicode
深入
正因为中文字符需要多个字节来表示,常见的正则表达式的文档就有可能无法覆盖这种情况。比如常见的资料都说,点号『.』可以匹配“除换行符\n之外的任意字符”,但这可能只适用于“单字节字符”
re.search(‘^.$‘, ‘发‘) == None #True
之所以会出现这种情况,是因为正则表达式无法正确将多个字节识别为“单个字符”
如果你细心就会发现,在Python 2.x中,我们指定的字符串使用Unicode编码,而文档里说了,正则表达式也可以指定Unicode模式的
正则表达式的操作可以简要概括为“用正则表达式去匹配字符串”,它涉及两个对象:正则表达式和字符串。
对字符串来说,如果没有设定Unicode模式,则多字节字符很可能会拆开为多个单字节字符对待(虽然它们并不是合法的ascii字符),Python 2.x中就是如此,“发”字在没有设定Unicode编码时,变成了3个单字节字符构成的字符串,点号『.』只能匹配其中的单个“字符”。如果显式将正则表达式设定为Unicode字符串(也就是在 u‘发‘ ),则“发”字视为单个字符,点号可以匹配。
那么对应的,如果你在正则表达式的字符组里使用了中文字符,表示正则表达式的字符串,也应该设定为Unicode字符串,否则正则表达式会认为字符组里不是单个字符
>>> re.search(‘^[我]$‘, u‘我‘) == None # True >>> re.search(u‘^[我]$‘, u‘我‘) == None # False
Python也可以指定正则表达式使用Unicode模式
不妨回头仔细想想你读过的文档,正则表达式中的『\d』和『\w』,都是如何解释的?或许你的第一反应是:『\d』等价于『[0-9]』,『\w』等价于『[0-9a-zA-Z_]』。因为有些文档说明了这种等价关系,有些文档却说:『\d』匹配数字字符,『\w』匹配单词字符。然而这只是针对ascii编码的规定,在Unicode编码中,全角数字0、1、2之类,应该也可以算“数字字符”,由『\d』匹配;中文的字符,应该也可以算“单词字符”,由『\w』匹配;同样的道理,中文的全角空格,应该也可以算作“空白字符”,由『\s』匹配。所以,如果你在Python中指定了正则表达式使用,『\d』、『\w』、『\s』就能匹配全角数字、中文字符、全角空格。
(字符均为全角) >>> re.search(‘(?u)^\d$‘, u‘1‘) == None # True >>> re.search(‘(?u)^\w$‘, u‘发‘) == None # True >>> re.search(‘(?u)^\s‘, u‘ ‘) == None # True
如果上面的数字用半角,
re.search(‘(?u)^\d$‘, u‘1‘) == None # False
参考博文:
http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html
http://www.crifan.com/summary_python_string_encoding_decoding_difference_and_comparation_python_2_x_str_unicode_vs_python_3_x_bytes_str/
http://www.infoq.com/cn/news/2011/02/regular-expressions-unicode/