收藏 分享(赏)

用Python作文本处理 第二章 -- 基本的字符串运用.doc

上传人:kpmy5893 文档编号:8529584 上传时间:2019-07-01 格式:DOC 页数:30 大小:295KB
下载 相关 举报
用Python作文本处理 第二章 -- 基本的字符串运用.doc_第1页
第1页 / 共30页
用Python作文本处理 第二章 -- 基本的字符串运用.doc_第2页
第2页 / 共30页
用Python作文本处理 第二章 -- 基本的字符串运用.doc_第3页
第3页 / 共30页
用Python作文本处理 第二章 -- 基本的字符串运用.doc_第4页
第4页 / 共30页
用Python作文本处理 第二章 -- 基本的字符串运用.doc_第5页
第5页 / 共30页
点击查看更多>>
资源描述

1、用 Python 作文本处理 /第二章目录 1 第一节 - 常用的操作 2 主题 - 快速排序 3 主题 - 排版 4 主题 - 处理字段 5 主题 - 字词数统计 6 主题 - 以二进制数据传送 ASCII 码信息 7 主题 - 词频统计第一节 - 常用的操作主题 - 快速排序排序是文字处理中大多数任务的关键所在。幸运的是,在 Python 里,使用.sort 的效率还不错。此外,在列表的任何不同对象都可以排序而不需要像 C 语言那样需要统一的元素(对于混合复数和 Unicode 字符串的列表排序在最近的几个 Python 版本里会触发TypeError 异常)。参考: complex+列表

2、排序的顺序有一种自然顺序,特别是不同类型混合的排序顺序都是 Python 的默认顺序。很多时候,你需要特定的顺序。特别是对于文本里的行做排序往往需要的不是简单的字母顺序。通常一行里有用的信息起始位置并不是第一个字符:人名里的姓往往是第二个单词;服务器日志里 IP 地址可能固定在某个字段;金额合计可能在每一行的第 70 列等等。只使用默认排序这些内容只会毫无意义。列表排序.sort()支持自定义比较函数参数。这个比较函数的功能是返回-1 则表示前者排在后者之前,返回 0 则表示二者顺序相同,返回 1 则表示后者排在前者之前。内置函数cmp()就是.sort()的默认比较函数(在速度上lst.so

3、rt()远远超过lst.sort(cmp))。对于不太长的列表使用自定义比较函数可以快速的解决问题。在很多情况下,甚至可以直接使用一个lambda表达式来完成任务。说到速度,使用自定义比较函数效率会很低。部分原因是 Python 的函数调用开销,函数本身也会增加花费的时间。不过有一种技术“Schwartzian 转换”可以加速这种自定义排序。Schwartzian 转换是兰德尔施瓦兹在 Perl 中最先开始使用的,但其中的技巧同样适用于 Python。使用 Schwartzian 转换主要包括三个步骤,(准确的来说这是 Guttman-Rosler 转换(GRT),同样基于 Schwartzi

4、an 转换): 1. 将列表转换为可以用默认排序的列表。 2. 使用.sort()排序。 3. 转回原先的格式。这项技术的主要作用是花费仅仅 O(2N)转换就可以使用默认的 O(N log N)排序。如果任务里排序时间是主要因素的话,使用这项技术将大大提高效率(唯一的限制就是转换花费的时间不会很多)。下面是一个简单的例子。排序比较方式是比较每一行的第四个单词。有的行单词数少于 4 个。测试文件约 20,000 行(1 兆左右)使用 Schwartzian 转换排序花费不到 2 秒,而使用自定义比较函数则花费 12 秒以上(排序结果一样)。确切时间不会很准确,但很明显效率提高了 6 倍。#- s

5、chwartzian_sort.py -#- 测试按第四个单词排序的速度#- 如果两行都有 4 个以上单词,则按照第 4 个第 5 个。来排序#- 没有 4 个单词的行排在有 4 个单词的行后面#- 没有 4 个单词的行之间按照默认顺序排列import sys, string, timewrerr = sys.stderr.write#- 自定义比较函数def fourth_word(ln1,ln2):lst1 = string.split(ln1)lst2 = string.split(ln2)#- 比较 4 个单词以上的行if len(lst1) = 4 and len(lst2) = 4

6、:return cmp(lst13:,lst23:)#- 少于 4 个单词的行排在后面elif len(lst1) = 4 and len(lst2) = 4:return 1else: # 默认顺序return cmp(ln1,ln2)#- 不计算读取时间lines = open(sys.argv1).readlines()#- 计时使用自定义比较函数排序start = time.time()lines.sort(fourth_word)end = time.time()wrerr(“Custom comparison func in %3.2f secsn“ % (end-start)#

7、open(tmp.custom,w).writelines(lines)#- 不计算读取时间lines = open(sys.argv1).readlines()#- 计时 Schwartzian 转换排序start = time.time()for n in range(len(lines): # 开始转换lst = string.split(linesn)if len(lst) = 4: # 把排序内容放在前面linesn = (lst3:, linesn)else: # 少于 4 个单词的行排在后面linesn = (377, linesn)lines.sort() # 排序for n

8、in range(len(lines): # 转换回原先内容linesn = linesn1end = time.time()wrerr(“Schwartzian transform sort in %3.2f secsn“ % (end-start)# open(tmp.schwartzian,w).writelines(lines)这只有一个特别的例子,但读者应该能够用任何形式来使用这种技术,特别是对于大文件。主题 - 排版虽然使用 ASCII 文本作为通讯格式并不好- 通常不会很复杂文件不会很大- 但其生命力还是很强的。README 文件,HOWTO 文件,电子邮件,新闻组,包括本书都仍

9、然是使用 ASCII 码文本(至少原文加工技术通常是很有价值的)。此外,许多像 HTML 和Latex 的格式往往也需要手动修改,清晰的排版是非常重要的。段落排版对于文本文件来说是极为常见的工作。Python2.3 增加了textwrap模块做一些有限的排版工作。在大多数情况下,这项工作可以使用文本编辑器来完成。不过,有时候自动化排版会更方便。这项工作很简单,比较奇怪的是,Python 没有相应的标准模块功能实现这一点。有一个formatter.DumbWriter类和formatter.AbstractWriter抽象类可以用于此项工作。相关讨论在第 5 章,坦率地说,使用这些类需要大量的定

10、制工作而且很复杂,往往不适合用于解决手头的任务。下面是一种简单的解决办法,可以当作命令行工具(从标准输入读取和输出到标准输出),或用于较大的应用程序。#- reformat_para.py -# 简单排版。主要用于左右对齐。LEFT,RIGHT,CENTER = LEFT,RIGHT,CENTERdef reformat_para(para=,left=0,right=72,just=LEFT):words = para.split()lines = line = word = 0end_words = 0while not end_words:if len(wordsword) right-

11、left: # 过长的单词line = wordswordword +=1if word = len(words):end_words = 1else: # 收集一行可以容纳的单词while len(line)+len(wordsword) = len(words):end_words = 1breaklines.append(line)line = if just=CENTER:r, l = right, leftreturn n.join( *left+ln.center(r-l) for ln in lines)elif just=RIGHT:return n.join(line.rju

12、st(right) for line in lines)else: # left justifyreturn n.join( *left+line for line in lines)if _name_=_main_:import sysif len(sys.argv) 1:c, w, l, p = 0, 0, 0, 0for pat in sys.argv1:for file in glob.glob(pat):s = open(file).read()wc = len(s), len(s.split(), len(s.split(n), len(s.split(nn)print t.joi

13、n(map(str, wc),t+filec, w, l, p = c+wc0, w+wc1, l+wc2, p+wc3wc = (c,w,l,p)print t.join(map(str, wc), tTOTALelse:s = sys.stdin.read()wc = len(s), len(s.split(), len(s.split(n), len(s.split(nn)print t.join(map(str, wc), tSTDIN这个小功能可以在一个函数里实现,不过它太过于紧凑。不过在使用解释器的环境下者只需要两行。上面的解决方法符合 Python 的准则,用一种显而易见的方式来

14、完成任务。还有个方法也可以完成同样的任务,读者可以思考一下(这只是好玩): wc = map(len,s+map(s.split,(None,n,nn)如果再加上print语句,就可以用一行来解决问题了。主题 - 以二进制数据传送 ASCII 码信息许多文本信息都是采用 7 位 ASCII 编码。特别是用在互联网传送的时候非 ASCII 码的信息需要先编成 7 位 ASCII 码才能正常传送,否则会因为最高位被分解导致出现乱码。像简单邮件传输协议(SMTP),网络新闻传输协议 (NNTP)或 HTTP 的内容编码等等都会需要用到编解码。编码 8 位二进制数据成 ASCII 码有一些通用的技术。

15、编码技术大都是将二进制数据转换成十六进制数据。UU 编码是一种很早的编码标准,主要用于新闻组和 BBS 传输二进制文件。Binhex 编码主要用于 Mac 的系统。base64是 MIME 的一种编码方式。这些编码技术基本上是在四个字节的 ASCII 码和三个字节的二进制数据之间转换,开始标记和结束标记等略有不同。QP 编码也是一种 MIME编码,不过它的编码长度是可变的,主要用于非 ASCII 码信息,对 7 位 ASCII 文本无需再次编码,仅将 8 位数据转换为 7 位。Python 提供了上述几种编码的相关模块。包装好的模块uu ,binhex,base64和quopri操作起来和文件

16、对象差不多。当然之间也略有不同。例如binhex是在编码以后才关闭输出文件,这使得它无法用于像cStringIO之类的文件对象。所有的这些编码工具都要使用到底层的 C 模块binascii 。实际上真正执行转换的是 binascii模块,不过它不会区分正确的编码块大小。标准库并没有提供通用的编解码功能,不过很容易就可以实现一个通用的编解码程序:#- encode_binary.py -# Provide encoders for arbitrary binary data# in Python strings. Handles block size issues# transparently,

17、 and returns a string.# Precompression of the input string can reduce# or eliminate any size penalty for encoding.import sysimport zlibimport binasciiUU = 45BASE64 = 57BINHEX = sys.maxintdef ASCIIencode(s=, type=BASE64, compress=1):“ASCII 文本转换为二进制数据“# 先选择编码方式if type = BASE64: encode = binascii.b2a_b

18、ase64elif type = UU: encode = binascii.b2a_uuelif type = BINHEX: encode = binascii.b2a_hqxelse: raise ValueError, “Encoding must be in UU, BASE64, BINHEX“# 再判断是否进行压缩if compress: s = press(s)# 一块一块开始编码offset = 0blocks = while 1:blocks.append(encode(soffset:offset+type)offset += typeif offset len(s):b

19、reak# 返回编码后的数据return .join(blocks)def ASCIIdecode(s=, type=BASE64, compress=1):“由二进制数据解码至 ASCII 文本“# 解码if type = BASE64: s = binascii.a2b_base64(s)elif type = BINHEX: s = binascii.a2b_hqx(s)elif type = UU:s = .join(binascii.a2b_uu(line) for line in s.split(n)# 判断是否进行解压缩if compress: s = zlib.decompre

20、ss(s)# 返回结果return s# Encode/decode STDIN for self-testif _name_ = _main_:decode, TYPE = 0, BASE64for arg in sys.argv:if arg.lower()=-d: decode = 1elif arg.upper()=UU: TYPE=UUelif arg.upper()=BINHEX: TYPE=BINHEXelif arg.upper()=BASE64: TYPE=BASE64if decode:print ASCIIdecode(sys.stdin.read(),type=TYPE

21、)else:print ASCIIencode(sys.stdin.read(),type=TYPE)上面的例子并没有包含任何头数据或需要的分界数据,这些可以通过使用uu,mimify 或MimeWriter来解决。或者你也可以编写自己需要的encode_binary.py.主题 - 词频统计分析文本里单词的出现频率是很有用的。在文字处理里,几乎要么是处理字节要么是处理单词。创建词频统计很简单,只需要使用 Python 字典就可以了,不过不是每个人都能立即找到最明显的解决方案。下面的这个例子有很好的通用性,提供了多种实用功能,并且还可用于命令行操作模式。#- histogram.py -# 统

22、计词频# 有几个工具函数可以提取需要的结果from string import split, maketrans, translate, punctuation, digitsimport sysfrom types import *import typesdef word_histogram(source):“对词作统计(不包括标点和数字) “hist = trans = maketrans(,)if type(source) in (StringType,UnicodeType): # 字符串for word in split(source):word = translate(word,

23、trans, punctuation+digits)if len(word) 0:histword = hist.get(word,0) + 1elif hasattr(source,read): # 文件try:from xreadlines import xreadlines # 测试是否可用for line in xreadlines(source):for word in split(line):word = translate(word, trans, punctuation+digits)if len(word) 0:histword = hist.get(word,0) + 1e

24、xcept ImportError: # 使用旧版本line = source.readline() # 速度慢,但内存占用少while line:for word in split(line):word = translate(word, trans, punctuation+digits)if len(word) 0:histword = hist.get(word,0) + 1line = source.readline()else:raise TypeError, “source must be a string-like or file-like object“return hist

25、def char_histogram(source, sizehint=1024*1024):hist = if type(source) in (StringType,UnicodeType): # 字符串for char in source:histchar = hist.get(char,0) + 1elif hasattr(source,read): # 文件chunk = source.read(sizehint)while chunk:for char in chunk:histchar = hist.get(char,0) + 1chunk = source.read(sizeh

26、int)else:raise TypeError, “source must be a string-like or file-like object“return histdef most_common(hist, num=1):pairs = for pair in hist.items():pairs.append(pair1,pair0)pairs.sort()pairs.reverse()return pairs:numdef first_things(hist, num=1):pairs = things = hist.keys()things.sort()for thing in

27、 things:pairs.append(thing,histthing)pairs.sort()return pairs:numif _name_ = _main_:if len(sys.argv) 1:hist = word_histogram(open(sys.argv1)else:hist = word_histogram(sys.stdin)print “Ten most common words:“for pair in most_common(hist, 10):print t, pair1, pair0print “First ten words alphabetically:

28、“for pair in first_things(hist, 10):print t, pair0, pair1# a more practical command-line version might use:# for pair in most_common(hist,len(hist):# print pair1,t,pair0几个设计的选择有些武断。词频统计里去除了标点符号,只保留字母,同时还区分大小写,这可能不是理想的选择。first_things()和most_common()只返回最初的几项排序结果。取自“http:/sunsol.wiki- 1 第二节 - 标准模块 2 主题

29、 - 字符串基本操作o 2.1 模块 - string : 字符串基本操作 2.1.1 常数: 2.1.2 函数:第二节 - 标准模块主题 - 字符串基本操作模块string是 Python 文本处理的核心模块。有很多其他模块会引用这个模块。string模块里大多数操作都在 Python1.6+复制到字符串对象上了。直接使用字符串对象的操作要比使用模块里相应函数的速度快一些。有些字符串对象的操作和模块里的并不等价,但这里仍会作以介绍。参考: str, UserString模块 - string : 字符串基本操作string模块(只有函数和常数,没有类)的函数都有一些共同的东西。 1. 字符串

30、是不可变对象。这意味着不能更改字符串自身(很多其他语言里,字符串操作都是直接更改自身的)。string 里模块函数都需要一个字符串作参数,然后返回一个新的字符串对象。通常的做法大多是使用赋值语句将返回结果绑定到同一个变量名称上。例如: import string str = “Mary had a little lamb“ str = string.replace(str, had, ate) strMary ate a little lamb第一个字符串对象并不会改变,但在第一次操作以后这个对象就不再和任何变量名称有关系了,在运行时这样的对象会作废料收集和处理。总之,string模块的函数不

31、会改变任何已有的字符串,但通过名称重新绑定可以看起来像作了改变。 2. 很多string模块的函数同样应用在字符串对象自身的方法上。使用字符串对象方法并不需要导入string模块,而且通常要比使用 string模块函数速度要快。这些大多数都是使用同一个方法实现的。 3. 一个建议是使用 string.join(string.split(.)来处理字符串分割和合并。更详细的介绍参看string.join() 和string.split()里的讨论。 4. 巧妙的使用 string.replace()模式。利用string.replace()可以填充字符串,有时结合其他技术会有惊人的表现。请参看s

32、tring.replace()的讨论和样例。 5. 一个可变的字符串可以通过使用内置的列表对象来实现(或array)。子串列表可以被修改或替换。使用array模块可以定义以字符为元素的字符串,同样具有索引下标操作和片操作。在需要完整字符串的时候用“.join()来建造一个真实的字符串。例如: lst = spam,and,eggs lst2 = toast print .join(lst)spamandtoast print .join(lst)spam and toast import array a = array.array(c,spam and eggs) print .join(a)

33、spam and eggs a0 = S print .join(a)Spam and eggs a-4: = array.array(c,toast) print .join(a)Spam and toast常数:string模块的常数定义了一些常用的字符集。所有这些常数本身就是个字符串。如果你需要的话,还可以建立需要的字符集。例如: import string string.brackets = “() print string.brackets() import string string.lowercaseabcdefghijklmnopqrstuvwxyz如果你需要使用特定的语言,不

34、要直接修改string.lowercase,可以定义一个新的常数,如string.spanish_lowercase(有些函数会使用到这个常量)。 string.uppercase大写字母。 import string string.uppercaseABCDEFGHIJKLMNOPQRSTUVWXYZ如果你需要使用特定的语言,不要直接修改string.uppercase,可以定义一个新的常数,如string.spanish_uppercase(有些函数会使用到这个常量)。 string.letters所有的字母。(string.lowercase+string.uppercase). str

35、ing.punctuation标点符号。 import string string.punctuation!“#$%?_| string.whitespace空白字符。包括 tab,换行,vtab ,回车,空格: import string string.whitespace011012013014015 不要修改string.whitespace(有些函数会使用到这个常量) 。 string.printable所有可以打印的字符(string.digits+string.letters+string.punctuation+string.whitespace)函数: string.atof(

36、s=.)不推荐使用。应该使用float() 。转换字符串为浮点数。参考: eval(), float() string.atoi(s=. ,base=10)Python2.0+不推荐使用。应该使用int()。转换字符串为一个整数(第二个参数是使用的进制)参考: eval(), int(), long() string.atol(s=. ,base=10)Python2.0+不推荐使用。应该使用long()。转换字符串为一个长整数(第二个参数是使用的进制)参考: eval(), long(), int() string.capitalize(s=.) “.capitalize()转换第一个字符为

37、大写,所有其他字符转换为小写: import string string.capitalize(“mary had a little lamb!“)Mary had a little lamb! string.capitalize(“Mary had a Little Lamb!“)Mary had a little lamb! string.capitalize(“2 Lambs had Mary!“)2 lambs had mary!Python1.6+,直接使用字符串对象的方法快一些。可以这样用: “mary had a little lamb“.capitalize()Mary had

38、 a little lamb参考: string.capwords(), string.lower() string.capwords(s=.) “.title()每个单词的第一个字符都转换为大写。同等的表达式是:#*- 等价于 -#string.join(map(string.capitalize,string.split(s)注意多余的空白会被消除: import string string.capwords(“mary HAD a little lamb!“)Mary Had A Little Lamb! string.capwords(“Mary had a Little Lamb!“

39、)Mary Had A Little Lamb!Python1.6+同等的字符串对象方法更名为“.title()。参考: string.capitalize(), string.lower(), “.istitle() string.center(s=., width=.) “.center(width)在前后填充空格使长度达到width(不会截断超出长度的字符串)。 import string string.center(width=30,s=“Mary had a little lamb“) Mary had a little lamb string.center(“Mary had a

40、little lamb“, 5)Mary had a little lambPython1.6+,直接使用字符串对象的方法快一些。可以这样用: “Mary had a little lamb“.center(25) Mary had a little lamb 参考: string.ljust(), string.rjust() string.count(s, sub ,start ,end) “.count(sub ,start ,end)统计匹配的子串个数。后两个参数用来限定比较的起止位置。 import string string.count(“mary had a little lam

41、b“, “a“)4 string.count(“mary had a little lamb“, “a“, 3, 10)2Python1.6+,直接使用字符串对象的方法快一些。可以这样用: mary had a little lamb.count(“a“)4 “.endswith(suffix ,start ,end)string模块里没有这个字符串对象方法。用来检查最后几个字符是否和参数suffix 一样。后两个参数用来限定比较的起止位置。参考: “.startswith(), string.find() string.expandtabs(s=. ,tabsize=8) “.expandt

42、abs(,tabsize=8)将 tab 换成空格。参数指定一个 tab 对应的空格长度。默认长度为 8。 import string s = mary011had a little lamb print smary had a little lamb string.expandtabs(s, 16)mary had a little lamb string.expandtabs(tabsize=1, s=s)mary had a little lambPython1.6+,直接使用字符串对象的方法快一些。可以这样用: mary011had a little lamb.expandtabs(2

43、5)mary had a little lamb string.find(s, sub ,start ,end) “.find(sub ,start ,end)查找子串第一次出现的位置。后两个参数用来限定比较的起止位置。如果未找到返回-1。 import string string.find(“mary had a little lamb“, “a“)1 string.find(“mary had a little lamb“, “a“, 3, 10)6 string.find(“mary had a little lamb“, “b“)21 string.find(“mary had a l

44、ittle lamb“, “b“, 3, 10)-1Python1.6+,直接使用字符串对象的方法快一些。可以这样用: mary had a little lamb.find(“ad“)6参考: string.index(), string.rfind() string.index(s, sub ,start ,end) “.index(sub ,start ,end)和string.find() 差不多,只是如果未找到会抛出ValueError而不是返回-1。 import string string.index(“mary had a little lamb“, “b“)21 string

45、.index(“mary had a little lamb“, “b“, 3, 10)Traceback (most recent call last):File “, line 1, in ?File “d:/py20sl/lib/string.py“, line 139, in indexreturn s.index(*args)ValueError: substring not found in string.indexPython1.6+,直接使用字符串对象的方法快一些。可以这样用: mary had a little lamb.index(“ad“)6参考: string.find

46、(), string.rindex().is*()函数用来检查字符串是否是由相应的字符组成的,string 模块里没有相应的函数: “.isalpha()是否都是字母。 “.isalnum()是否都是字母加数字。 “.isdigit()是否都是数字字符。 “.islower()所含字母是否都是小写字母: “ab123“.islower(), 123.islower(), Ab123.islower()(1, 0, 0)参考: “.lower() “.isspace()是否都是空白字符。 “.istitle()每个单词是否第一个字符都是大写其他小写。参考: “.title() “.isupper()所含字母是否都是大写字母:参考: “.upper() string.join(words=. ,sep=“ “) “.join(words)连接列表里各个子字符串组成一个新字符串,参数sep是间隔使用的字符串。值得注意的是,string.join()和string.split() 函数是互逆的。换句话说string.join(string.split(s,sep),sep)=s肯定是真。通常情况下,string.join() 中使用都是自然生成的字符串列表。举例来说,这个小程序里的列表就

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报