当前位置:首页 >> 其它课程 >> Python作文本处理

Python作文本处理


用 Python 作文本处理/第二章

目录 ? ? ? ? ? ? ? 1 2 3 4 5 6 7 第一节 -- 常用的操作 主题 -- 快速排序 主题 -- 排版 主题 -- 处理字段 主题 -- 字词数统计 主题 -- 以二进制数据传送 ASCII 码信息 主题 -- 词频统计

第一节 -- 常用的操作 主题 -- 快速排序 排序是

文字处理中大多数任务的关键所在。幸运的是,在 Python 里,使用`[].sort`的效 率还不错。此外,在列表的任何不同对象都可以排序而不需要像 C 语言那样需要统一的 元素 (对于混合复数和 Unicode 字符串的列表排序在最近的几个 Python 版本里会触发 'TypeError'异常)。 参考: [complex] +++ 列表排序的顺序有一种自然顺序,特别是不同类型混合的排序顺序都是 Python 的默认 顺序。很多时候,你需要特定的顺序。特别是对于文本里的行做排序往往需要的不是简 单的字母顺序。通常一行里有用的信息起始位置并不是第一个字符:人名里的姓往往是 第二个单词;服务器日志里 IP 地址可能固定在某个字段;金额合计可能在每一行的第 70 列等等。只使用默认排序这些内容只会毫无意义。 列表排序`[].sort()`支持自定义比较函数参数。 这个比较函数的功能是返回-1 则表示前者 排在后者之前,返回 0 则表示二者顺序相同,返回 1 则表示后者排在前者之前。内置函 数`cmp()`就是`[].sort()`的默认比较函数 (在速度上'lst.sort()'远远超过'lst.sort(cmp)') 。 对于不太长的列表使用自定义比较函数可以快速的解决问题。在很多情况下,甚至可以 直接使用一个'lambda'表达式来完成任务。 说到速度,使用自定义比较函数效率会很低。部分原因是 Python 的函数调用开销,函 数本身也会增加花费的时间。不过有一种技术“Schwartzian 转换”可以加速这种自定 义排序。Schwartzian 转换是兰德尔施瓦兹在 Perl 中最先开始使用的,但其中的技巧同 样适用于 Python。 使用 Schwartzian 转换主要包括三个步骤,(准确的来说这是 Guttman-Rosler 转换 (GRT),同样基于 Schwartzian 转换): ? ? ? 1. 将列表转换为可以用默认排序的列表。 2. 使用`[].sort()`排序。 3. 转回原先的格式。 这项技术的主要作用是花费仅仅 O(2N)转换就可以使用默认的 O(N log N)排序。如果 任务里排序时间是主要因素的话,使用这项技术将大大提高效率(唯一的限制就是转换 花费的时间不会很多)。

下面是一个简单的例子。排序比较方式是比较每一行的第四个单词。有的行单词数少于 4 个。测试文件约 20,000 行(1 兆左右)使用 Schwartzian 转换排序花费不到 2 秒, 而使用自定义比较函数则花费 12 秒以上(排序结果一样)。确切时间不会很准确,但 很明显效率提高了 6 倍。 #---------- schwartzian_sort.py ----------# #- 测试按第四个单词排序的速度 #- 如果两行都有 4 个以上单词,则按照第 4 个第 5 个。。来排序 #- 没有 4 个单词的行排在有 4 个单词的行后面 #- 没有 4 个单词的行之间按照默认顺序排列 import sys, string, time wrerr = 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: return cmp(lst1[3:],lst2[3:]) #-- 少于 4 个单词的行排在后面 elif len(lst1) >= 4 and len(lst2) < 4: return -1 #-- 少于 4 个单词的行排在后面 elif len(lst1) < 4 and len(lst2) >= 4: return 1 else: # 默认顺序 return cmp(ln1,ln2) #- 不计算读取时间 lines = open(sys.argv[1]).readlines() #- 计时使用自定义比较函数排序 start = time.time() lines.sort(fourth_word) end = time.time() wrerr("Custom comparison func in %3.2f secs\n" % (end-start)) # open('tmp.custom','w').writelines(lines) #- 不计算读取时间 lines = open(sys.argv[1]).readlines() #- 计时 Schwartzian 转换排序 start = time.time() for n in range(len(lines)): # 开始转换 lst = string.split(lines[n]) if len(lst) >= 4: # 把排序内容放在前面 lines[n] = (lst[3:], lines[n])

else: # 少于 4 个单词的行排在后面 lines[n] = (['\377'], lines[n]) lines.sort() for n in range(len(lines)): lines[n] = lines[n][1] # 排序 # 转换回原先内容

end = time.time() wrerr("Schwartzian transform sort in %3.2f secs\n" % (end-start)) # open('tmp.schwartzian','w').writelines(lines) 这只有一个特别的例子,但读者应该能够用任何形式来使用这种技术,特别是对于大文 件。 主题 -- 排版 虽然使用 ASCII 文本作为通讯格式并不好--通常不会很复杂文件不会很大--但其生命力 还是很强的。README 文件,HOWTO 文件,电子邮件,新闻组,包括本书都仍然是 使用 ASCII 码文本(至少原文加工技术通常是很有价值的)。此外,许多像 HTML 和 Latex 的格式往往也需要手动修改,清晰的排版是非常重要的。 段落排版对于文本文件来说是极为常见的工作。Python2.3 增加了[textwrap]模块做一 些有限的排版工作。在大多数情况下,这项工作可以使用文本编辑器来完成。不过,有 时候自动化排版会更方便。这项工作很简单,比较奇怪的是,Python 没有相应的标准 模块功能实现这一点。有一个`formatter.DumbWriter`类和 `formatter.AbstractWriter`抽象类可以用于此项工作。相关讨论在第 5 章,坦率地说, 使用这些类需要大量的定制工作而且很复杂,往往不适合用于解决手头的任务。 下面是一种简单的解决办法,可以当作命令行工具(从标准输入读取和输出到标准输 出),或用于较大的应用程序。 #---------- reformat_para.py ----------# # 简单排版。主要用于左右对齐。 LEFT,RIGHT,CENTER = 'LEFT','RIGHT','CENTER' def reformat_para(para=,left=0,right=72,just=LEFT): words = para.split() lines = [] line = word = 0 end_words = 0 while not end_words: if len(words[word]) > right-left: # 过长的单词 line = words[word] word +=1 if word >= len(words): end_words = 1 else: # 收集一行可以容纳的单词 while len(line)+len(words[word]) <= right-left: line += words[word]+' ' word += 1

if word >= len(words): end_words = 1 break lines.append(line) line = if just==CENTER: r, l = right, left return '\n'.join([' '*left+ln.center(r-l) for ln in lines]) elif just==RIGHT: return '\n'.join([line.rjust(right) for line in lines]) else: # left justify return '\n'.join([' '*left+line for line in lines]) if __name__=='__main__': import sys if len(sys.argv) <> 4: print "Please specify left_margin, right_marg, justification" else: left = int(sys.argv[1]) right = int(sys.argv[2]) just = sys.argv[3].upper() # 排版每一段 for p in sys.stdin.read().split('\n\n'): print reformat_para(p,left,right,just),'\n' 留给读者一些改进任务。例如您可能需要首行缩进。或者有些段落需要的格式不适合用 此排版(例如题头等等)。具体的应用程序还可能需要确定如何分段等等。 主题 -- 处理字段 数据表,DBMS,日志文件以及平面数据库往往在每行放置同样的纪录,每条记录有相 同的字段。通常这些字段要么是用分割符间隔要么是用固定位置来存放。 分析这些记录的结构很容易,进行表格计算上也同样很简单。对于各种文本结构数据, 可以使用几乎相同的代码来做处理。 下面的例子中提供了一种通用的框架来处理结构化文本。 #---------- fields_stats.py ----------# # 处理文本数据库里的多个字段 import operator from types import * from xreadlines import xreadlines # 需要 Python2.1,提高效率 # 2.1 以下使用.readline() #-- 格式常量 DELIMITED = 1 FLATFILE = 2 #-- 一些简单的处理过程(使用函数式风格) nillFunc = lambda lst: None toFloat = lambda lst: map(float, lst)

avg_lst = lambda lst: reduce(operator.add, toFloat(lst))/len(lst) sum_lst = lambda lst: reduce(operator.add, toFloat(lst)) max_lst = lambda lst: reduce(max, toFloat(lst)) class FieldStats: """统计资料 text_db 可以是字符串(包括 Unicode 字符串)或文件类对象 style 有 2 种格式(DELIMITED, FLATFILE)分隔符或位置 默认使用分隔符格式 column_positions 位置列表,第一列的位置为 1。 例如:(1,7,40) 表示 3 个字段,起始位置分别为 1,7,40 field_funcs 是字典,储存需要处理的字段和对应处理过程。 例如:{1:avg_lst, 4:sum_lst, 5:max_lst} 表示对第一个字段做求平均值处理 对第四个字段做合计处理,对第 5 个字段求最大值 其他字段不做处理。 """ def __init__(self, text_db=, style=DELIMITED, delimiter=',', column_positions=(1,), field_funcs={} ): self.text_db = text_db self.style = style self.delimiter = delimiter self.column_positions = column_positions self.field_funcs = field_funcs def calc(self): """计算""" #-- 第一步先建立列表的列表来存放数据。 used_cols = self.field_funcs.keys() used_cols.sort() # : 不使用 column[0] columns = [] for n in range(1+used_cols[-1]): # 提示: 这里可以使用'[[]]*num'来代替 columns.append([]) #-- 第二步生成需要计算的列表数据 # text_db 是字符串对象 if type(self.text_db) in (StringType,UnicodeType): for line in self.text_db.split('\n'): fields = self.splitter(line) for col in used_cols: field = fields[col-1] # 注意这里是由 0 开始的索引 columns[col].append(field)

else: # text_db 是文件对象 for line in xreadlines(self.text_db): fields = self.splitter(line) for col in used_cols: field = fields[col-1] # 注意这里是由 0 开始的索引 columns[col].append(field) #-- 第三步作处理计算 results = [None] * (1+used_cols[-1]) for col in used_cols: results[col] = \ apply(self.field_funcs[col],(columns[col],)) #-- Finally, return the result list return results def splitter(self, line): """分解一行为字段表""" if self.style == DELIMITED: return line.split(self.delimiter) elif self.style == FLATFILE: fields = [] # 注意这里是以 0 开始的索引 # 最后加上结束位置 num_positions = len(self.column_positions) offsets = [(pos-1) for pos in self.column_positions] offsets.append(len(line)) for pos in range(num_positions): start = offsets[pos] end = offsets[pos+1] fields.append(line[start:end]) return fields else: raise ValueError, \ "Text database must be DELIMITED or FLATFILE" #-- 测试数据 # First Name, Last Name, Salary, Years Seniority, Department delim = Kevin,Smith,50000,5,Media Relations Tom,Woo,30000,7,Accounting Sally,Jones,62000,10,Management .strip() # no leading/trailing newlines # Comment First flat = tech note Kevin more filler Tom yet more... Sally Last Smith Woo Jones Salary 50000 30000 62000 Years 5 7 10 Dept Media Relations Accounting Management

.strip()

# no leading/trailing newlines

#-- Run self-test code if __name__ == '__main__': getdelim = FieldStats(delim, field_funcs={3:avg_lst,4:max_lst}) print 'Delimited Calculations:' results = getdelim.calc() print ' Average salary -', results[3] print ' Max years worked -', results[4] getflat = FieldStats(flat, field_funcs={3:avg_lst,4:max_lst}, style=FLATFILE, column_positions=(15,25,35,45,52)) print 'Flat Calculations:' results = getflat.calc() print ' Average salary -', results[3] print ' Max years worked -', results[4] 上面的例子中包括了一些效率上的考虑,可以用于大型数据集。首先,'FieldStats'类是 动态处理文本,而没有保存整个数据。`xreadlines.xreadlines()`是一个极其快速和有效 的行阅读器,但它需要 Python2.1+,其他版本可以使用`FILE.readline()`或 `FILE.readlines()`。此外,只关心实际上需要用到的数据,以便节省内存。这里没有考 虑同时使用多个相关字段做处理的应用,不过可以提取出数据作再次运算。 还有就是没有考虑在同一个字段上作多种处理,这个留给读者作思考。 主题 -- 字词数统计 Unix 系统下有个工具'wc'可以做这个任务。这是个基本工具不能统计段落。'wc'只能统 计字符,单词和行。有几个命令选项可以控制显示的结果,不过我很少用到它们。 在写这一段的时候,我发现自己的系统里没有装'wc'。下面的例子实际上是一个增强版 'wc',不过缺少了很多命令行选项。在 Python 里可以非常简单的实现'wc'的功能。其 主要使用的技术是`"".join()`和`"".split()`。 #---------- wc.py ----------# # 统计字符数,单词数,行数和段落数 # 可以 STDIN 输入或使用文件通配符 import sys, glob if len(sys.argv) > 1: c, w, l, p = 0, 0, 0, 0 for pat in sys.argv[1:]: for file in glob.glob(pat): s = open(file).read() wc = len(s), len(s.split()), \ len(s.split('\n')), len(s.split('\n\n')) print '\t'.join(map(str, wc)),'\t'+file c, w, l, p = c+wc[0], w+wc[1], l+wc[2], p+wc[3] wc = (c,w,l,p) print '\t'.join(map(str, wc)), '\tTOTAL' else: s = sys.stdin.read() wc = len(s), len(s.split()), len(s.split('\n')), \

len(s.split('\n\n')) print '\t'.join(map(str, wc)), '\tSTDIN' 这个小功能可以在一个函数里实现,不过它太过于紧凑。不过在使用解释器的环境下者 只需要两行。 上面的解决方法符合 Python 的准则,用一种显而易见的方式来完成任务。还有个方法 也可以完成同样的任务,读者可以思考一下(这只是好玩): >>> wc = map(len,[s]+map(s.split,(None,'\n','\n\n'))) 如果再加上'print'语句,就可以用一行来解决问题了。 主题 -- 以二进制数据传送 ASCII 码信息 许多文本信息都是采用 7 位 ASCII 编码。 特别是用在互联网传送的时候非 ASCII 码的信 息需要先编成 7 位 ASCII 码才能正常传送,否则会因为最高位被分解导致出现乱码。像 简单邮件传输协议(SMTP),网络新闻传输协议(NNTP)或 HTTP 的内容编码等等都会需 要用到编解码。编码 8 位二进制数据成 ASCII 码有一些通用的技术。 编码技术大都是将二进制数据转换成十六进制数据。UU 编码是一种很早的编码标准, 主要用于新闻组和 BBS 传输二进制文件。Binhex 编码主要用于 Mac 的系统。base64 是 MIME 的一种编码方式。 这些编码技术基本上是在四个字节的 ASCII 码和三个字节的 二进制数据之间转换, 开始标记和结束标记等略有不同。 QP 编码也是一种 MIME 编码, 不过它的编码长度是可变的,主要用于非 ASCII 码信息,对 7 位 ASCII 文本无需再次编 码,仅将 8 位数据转换为 7 位。 Python 提供了上述几种编码的相关模块。包装好的模块[uu],[binhex],[base64]和 [quopri]操作起来和文件对象差不多。当然之间也略有不同。例如[binhex]是在编码以 后才关闭输出文件,这使得它无法用于像[cStringIO]之类的文件对象。所有的这些编码 工具都要使用到底层的 C 模块[binascii]。实际上真正执行转换的是[binascii]模块,不 过它不会区分正确的编码块大小。 标准库并没有提供通用的编解码功能,不过很容易就可以实现一个通用的编解码程序: #---------- encode_binary.py ----------# # Provide encoders for arbitrary binary data # in Python strings. Handles block size issues # transparently, and returns a string. # Precompression of the input string can reduce # or eliminate any size penalty for encoding. import sys import zlib import binascii UU = 45 BASE64 = 57 BINHEX = sys.maxint def ASCIIencode(s=, type=BASE64, compress=1): """ASCII 文本转换为二进制数据""" # 先选择编码方式 if type == BASE64: encode = binascii.b2a_base64 elif type == UU: encode = binascii.b2a_uu elif type == BINHEX: encode = binascii.b2a_hqx else: raise ValueError, "Encoding must be in UU, BASE64, BINHEX"

# 再判断是否进行压缩 if compress: s = zlib.compress(s) # 一块一块开始编码 offset = 0 blocks = [] while 1: blocks.append(encode(s[offset:offset+type])) offset += type if offset > len(s): break # 返回编码后的数据 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.decompress(s) # 返回结果 return s # Encode/decode STDIN for self-test if __name__ == '__main__': decode, TYPE = 0, BASE64 for arg in sys.argv: if arg.lower()=='-d': decode = 1 elif arg.upper()=='UU': TYPE=UU elif arg.upper()=='BINHEX': TYPE=BINHEX elif arg.upper()=='BASE64': TYPE=BASE64 if decode: print ASCIIdecode(sys.stdin.read(),type=TYPE) else: print ASCIIencode(sys.stdin.read(),type=TYPE) 上面的例子并没有包含任何头数据或需要的分界数据,这些可以通过使用[uu],[mimify] 或[MimeWriter]来解决。或者你也可以编写自己需要的'encode_binary.py'. 主题 -- 词频统计 分析文本里单词的出现频率是很有用的。在文字处理里,几乎要么是处理字节要么是处 理单词。创建词频统计很简单,只需要使用 Python 字典就可以了,不过不是每个人都 能立即找到最明显的解决方案。 下面的这个例子有很好的通用性, 提供了多种实用功能, 并且还可用于命令行操作模式。 #---------- histogram.py ----------# # 统计词频 # 有几个工具函数可以提取需要的结果

from string import split, maketrans, translate, punctuation, digits import sys from types import * import types def word_histogram(source): """对词作统计(不包括标点和数字)""" hist = {} trans = maketrans(,) if type(source) in (StringType,UnicodeType): # 字符串 for word in split(source): word = translate(word, trans, punctuation+digits) if len(word) > 0: hist[word] = hist.get(word,0) + 1 elif 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: hist[word] = hist.get(word,0) + 1 except ImportError: # 使用旧版本 line = source.readline() # 速度慢,但内存占用少 while line: for word in split(line): word = translate(word, trans, punctuation+digits) if len(word) > 0: hist[word] = hist.get(word,0) + 1 line = source.readline() else: raise TypeError, \ "source must be a string-like or file-like object" return hist def char_histogram(source, sizehint=1024*1024): hist = {} if type(source) in (StringType,UnicodeType): # 字符串 for char in source: hist[char] = hist.get(char,0) + 1 elif hasattr(source,'read'): # 文件 chunk = source.read(sizehint) while chunk: for char in chunk: hist[char] = hist.get(char,0) + 1 chunk = source.read(sizehint) else: raise TypeError, \

"source must be a string-like or file-like object" return hist def most_common(hist, num=1): pairs = [] for pair in hist.items(): pairs.append((pair[1],pair[0])) pairs.sort() pairs.reverse() return pairs[:num] def first_things(hist, num=1): pairs = [] things = hist.keys() things.sort() for thing in things: pairs.append((thing,hist[thing])) pairs.sort() return pairs[:num] if __name__ == '__main__': if len(sys.argv) > 1: hist = word_histogram(open(sys.argv[1])) else: hist = word_histogram(sys.stdin) print "Ten most common words:" for pair in most_common(hist, 10): print '\t', pair[1], pair[0] print "First ten words alphabetically:" for pair in first_things(hist, 10): print '\t', pair[0], pair[1] # a more practical command-line version might use: # for pair in most_common(hist,len(hist)): # print pair[1],'\t',pair[0] 几个设计的选择有些武断。词频统计里去除了标点符号,只保留字母,同时还区分大小 写,这可能不是理想的选择。'first_things()'和'most_common()'只返回最初的几项排 序结果。 取自"http://sunsol.wiki-site.com/index.php/Tpip_210"

? ?

1 第二节 -- 标准模块 2 主题 -- 字符串基本操作 o 2.1 模块 -- string : 字符串基本操作 ? 2.1.1 常数: ? 2.1.2 函数:

第二节 -- 标准模块 主题 -- 字符串基本操作 模块[string]是 Python 文本处理的核心模块。 有很多其他模块会引用这个模块。 [string] 模块里大多数操作都在 Python1.6+复制到字符串对象上了。直接使用字符串对象的操 作要比使用模块里相应函数的速度快一些。有些字符串对象的操作和模块里的并不等 价,但这里仍会作以介绍。 参考: [str], [UserString] 模块 -- string : 字符串基本操作 [string]模块(只有函数和常数,没有类)的函数都有一些共同的东西。 ? 1. 字符串是不可变对象。这意味着不能更改字符串自身(很多其他语言里,字符串操作 都是直接更改自身的)。[string]里模块函数都需要一个字符串作参数,然后返回一个新 的字符串对象。通常的做法大多是使用赋值语句将返回结果绑定到同一个变量名称上。 例如: >>> import string >>> str = "Mary had a little lamb" >>> str = string.replace(str, 'had', 'ate') >>> str 'Mary ate a little lamb' 第一个字符串对象并不会改变,但在第一次操作以后这个对象就不再和任何变量名称有 关系了,在运行时这样的对象会作废料收集和处理。总之,[string]模块的函数不会改变 任何已有的字符串,但通过名称重新绑定可以看起来像作了改变。 ? 2. 很多[string]模块的函数同样应用在字符串对象自身的方法上。使用字符串对象方法 并不需要导入[string]模块,而且通常要比使用[string]模块函数速度要快。这些大多数 都是使用同一个方法实现的。 3. 一个建议是使用'string.join(string.split(...))'来处理字符串分割和合并。更详细的介 绍参看`string.join()`和`string.split()`里的讨论。 4. 巧妙的使用`string.replace()`模式。利用`string.replace()`可以填充字符串,有时结 合其他技术会有惊人的表现。请参看`string.replace()`的讨论和样例。 5. 一个可变的字符串可以通过使用内置的列表对象来实现(或[array])。子串列表可以 被修改或替换。使用[array]模块可以定义以字符为元素的字符串,同样具有索引下标操 作和片操作。在需要完整字符串的时候用`"".join()`来建造一个真实的字符串。例如: >>> lst = ['spam','and','eggs'] >>> lst[2] = 'toast'

? ? ?

>>> print .join(lst) spamandtoast >>> print ' '.join(lst) spam and toast

>>> import array >>> a = array.array('c','spam and eggs') >>> print .join(a) spam and eggs >>> a[0] = '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 []{}()<> ? string.digits 十进制数字("0123456789"). ? string.hexdigits 十六进制数字("0123456789abcdefABCDEF"). ? string.octdigits 八进制数字("01234567"). ? string.lowercase 小写字母。 >>> import string >>> string.lowercase 'abcdefghijklmnopqrstuvwxyz' 如果你需要使用特定的语言, 不要直接修改`string.lowercase`, 可以定义一个新的常数, 如'string.spanish_lowercase'(有些函数会使用到这个常量)。 ? string.uppercase

大写字母。 >>> import string >>> string.uppercase 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 如果你需要使用特定的语言,不要直接修改`string.uppercase`,可以定义一个新的常 数,如'string.spanish_uppercase'(有些函数会使用到这个常量)。 ? string.letters 所有的字母。(string.lowercase+string.uppercase). ? string.punctuation 标点符号。 >>> import string >>> string.punctuation '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' ? string.whitespace 空白字符。包括 tab,换行,vtab,回车,空格: >>> import string >>> string.whitespace '\011\012\013\014\015 ' 不要修改`string.whitespace`(有些函数会使用到这个常量) 。 ? string.printable 所有可以打印的字符 (string.digits+string.letters+string.punctuation+string.whitespace) 函数: ? string.atof(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() 转换第一个字符为大写,所有其他字符转换为小写: >>> 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 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!") '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 little lamb", 5) 'Mary had a little lamb' Python1.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 lamb", "a") 4 >>> string.count("mary had a little lamb", "a", 3, 10) 2 Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> 'mary had a little lamb'.count("a") 4 ? "".endswith(suffix [,start [,end]]) [string]模块里没有这个字符串对象方法。用来检查最后几个字符是否和参数 'suffix'一 样。后两个参数用来限定比较的起止位置。 参考: `"".startswith()`, `string.find()` ? ? string.expandtabs(s=... [,tabsize=8]) "".expandtabs([,tabsize=8]) 将 tab 换成空格。参数指定一个 tab 对应的空格长度。默认长度为 8。 >>> import string >>> s = 'mary\011had a little lamb' >>> print s mary had a little lamb >>> string.expandtabs(s, 16) 'mary had a little lamb' >>> string.expandtabs(tabsize=1, s=s) 'mary had a little lamb' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> 'mary\011had a little lamb'.expandtabs(25) '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 little lamb", "b", 3, 10) -1 Python1.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.index("mary had a little lamb", "b", 3, 10) Traceback (most recent call last): File "<stdin>", line 1, in ? File "d:/py20sl/lib/string.py", line 139, in index return s.index(*args) ValueError: substring not found in string.index Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> 'mary had a little lamb'.index("ad") 6 参考: `string.find()`, `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()`中使用都是自然生成的字符串列表。举例来说,这个小程序 里的列表就是按输入顺序构成的: #---------- list_capwords.py ----------# import string,sys capwords = [] for line in sys.stdin.readlines(): for word in line.split(): if word == word.upper() and word.isalpha(): capwords.append(word) print string.join(capwords, '\n') 在'list_capwords.py'里利用连接使用字符串的效率还不错。如果使用 Python2.0 里增 加的加法操作则会降低执行速度: >>> import string >>> s = "Mary had a little lamb" >>> t = "its fleece was white as snow" >>> s = s +" "+ t # 效率一般,如果是长字符串会较低 >>> s += " " + t # 效率低 >>> lst = [s] >>> lst.append(t) # 效率更低 >>> s = string.join(lst) >>> s = string.join((s," ",t)) # 效率高 Python1.6+,使用`"".join()`的效率是最高的。 参考: `string.split()` ? string.joinfields(...) 和`string.join()`一样。 ? ? string.ljust(s=..., width=...) "".ljust(width) 后补空格。(不会截断字符串) >>> import string >>> string.ljust(width=30,s="Mary had a little lamb") 'Mary had a little lamb ' >>> string.ljust("Mary had a little lamb", 5) 'Mary had a little lamb' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a little lamb".ljust(25) 'Mary had a little lamb '

参考: `string.rjust()`, `string.center()` ? ? string.lower(s=...) "".lower() 转换大写字母为小写。 >>> import string >>> string.lower("mary HAD a little lamb!") 'mary had a little lamb!' >>> string.lower("Mary had a Little Lamb!") 'mary had a little lamb!' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a Little Lamb!".lower() 'mary had a little lamb!' 参考: `string.upper()` ? ? string.lstrip(s=...) "".lstrip([chars=string.whitespace]) 删除左边的空白字符。Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> import string >>> s = """ ... Mary had a little lamb \011""" >>> string.lstrip(s) 'Mary had a little lamb \011' >>> s.lstrip() 'Mary had a little lamb \011' Python2.3+可以指定需要删除的字符。 参考: `string.rstrip(), `string.strip()` ? string.maketrans(from, to) 制作字符转换表,用于`string.translate()`。参数'from'和'to'必须是相同的长度。转换 表是一个长度为 256 的字符串,每个字符对应相应的字节值。 >>> import string >>> ord('A') 65 >>> ord('z') 122 >>> string.maketrans('ABC','abc')[65:123] 'abcDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz' >>> string.maketrans('ABCxyz','abcXYZ')[65:123] 'abcDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwXYZ' 参考: `string.translate()` ? ? string.replace(s=..., old=..., new=... [,maxsplit=...]) "".replace(old, new [,maxsplit]) 替换操作。最后一个参数限定最大替换次数。

>>> import string >>> string.replace("Mary had a little lamb", "a little", "some") 'Mary had some lamb' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a little lamb".replace("a little", "some") 'Mary had some lamb' 使用`string.replace()`的多次替换时需要注意是否把不期望替换的也替换了。有时可以 使用保留字符串来解决这个问题。当然使用正则表达式也可以达到同样的目的,但是使 用`string.replace()`会更快一些: >>> import string >>> line = 'variable = val # see comments #3 and #4' >>> # 现在想把'#3'和'#4'中的'#'替换为'number' >>> string.replace(line,'#','number ') # 没成功 'variable = val number see comments number 3 and number 4' >>> place_holder=string.replace(line,' # ',' !!! ') # 保留不替换的 >>> place_holder 'variable = val !!! see comments #3 and #4' >>> place_holder=place_holder.replace('#','number ') # 替换完了 >>> place_holder 'variable = val !!! see comments number 3 and number 4' >>> line = string.replace(place_holder,'!!!','#') # 再换回原来的 >>> line 'variable = val # see comments number 3 and number 4' 显然,对于这样的工作,需要仔细处理。特别是要注意作为保留替换的字符串是否是子 串。 参考: `string.translate()`, `mx.TextTools.replace()` ? ? string.rfind(s, sub [,start [,end]]) "".rfind(sub [,start [,end]]) 从右边开始查找子串第一次出现的位置。后两个参数用来限定比较的起止位置。如果未 找到返回-1。 >>> import string >>> string.rfind("mary had a little lamb", "a") 19 >>> string.rfind("mary had a little lamb", "a", 3, 10) 9 >>> string.rfind("mary had a little lamb", "b") 21 >>> string.rfind("mary had a little lamb", "b", 3, 10) -1 Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> 'mary had a little lamb'.rfind("ad") 6 参考: `string.rindex()`, `string.find()` ? ? string.rindex(s, sub [,start [,end]]) "".rindex(sub [,start [,end]])

和`string.rfind()`差不多,只是如果未找到会抛出'ValueError'而不是返回-1。 >>> import string >>> string.rindex("mary had a little lamb", "b") 21 >>> string.rindex("mary had a little lamb", "b", 3, 10) Traceback (most recent call last): File "<stdin>", line 1, in ? File "d:/py20sl/lib/string.py", line 148, in rindex return s.rindex(*args) ValueError: substring not found in string.rindex Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> 'mary had a little lamb'.index("ad") 6 参考: `string.rfind()`, `string.index()` ? ? string.rjust(s=..., width=...) "".rjust(width) 前补空格。(不会截断字符串) >>> import string >>> string.rjust(width=30,s="Mary had a little lamb") ' Mary had a little lamb' >>> string.rjust("Mary had a little lamb", 5) 'Mary had a little lamb' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a little lamb".rjust(25) ' Mary had a little lamb' 参考: `string.ljust()`, `string.center()` ? ? string.rstrip(s=...) "".rstrip() 删除右边的空白字符。Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> import string >>> s = """ ... Mary had a little lamb \011""" >>> string.rstrip(s) '\012 Mary had a little lamb' >>> s.rstrip() '\012 Mary had a little lamb' Python2.3+可以指定需要删除的字符。 参考: `string.lstrip(), `string.strip()` ? ? string.split(s=... [,sep=... [,maxsplit=...]]) "".split([,sep [,maxsplit]]) 分割字符串为子串列表。参数'sep'可以指定分割符,默认为空白。分隔符不会出现在返 回结果列表里。'maxsplit'限定了最大分割数。多余的未分割字符串会附在列表最后面。 >>> import string

>>> s = 'mary had a little lamb ...with a glass of sherry' >>> string.split(s, ' a ') ['mary had', 'little lamb ...with', 'glass of sherry'] >>> string.split(s) ['mary', 'had', 'a', 'little', 'lamb', '...with', 'a', 'glass', 'of', 'sherry'] >>> string.split(s,maxsplit=5) ['mary', 'had', 'a', 'little', 'lamb', '...with a glass of sherry'] Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a Little Lamb!".split() ['Mary', 'had', 'a', 'Little', 'Lamb!'] `string.split()`函数相当有用, 特别是用在分割单词的时候。 利用这个函数可以快速分词: >>> wc = lambda s: len(s.split()) >>> wc("Mary had a Little Lamb") 5 >>> s = """Mary had a Little Lamb ... its fleece as white as snow. ... And everywhere that Mary went ... the lamb was sure to go.""" >>> print s Mary had a Little Lamb its fleece as white as snow. And everywhere that Mary went ... the lamb was sure to go. >>> wc(s) 23 `string.split()`往往和`string.join()`结合使用。 常见的模式是先分解, 再处理, 最后合并。 例如使用这个模式来去掉多余的空白: >>> import string >>> s = """Mary had a Little Lamb ... its fleece as white as snow. ... And everywhere that Mary went ... the lamb was sure to go.""" >>> string.join(string.split(s)) 'Mary had a Little Lamb its fleece as white as snow. And everywhere ... that Mary went the lamb was sure to go.' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "-".join(s.split()) 'Mary-had-a-Little-Lamb-its-fleece-as-white-as-snow.-And-everywhere ...-that-Mary-went--the-lamb-was-sure-to-go.' 参 考: `string.join()`, `mx.TextTools.setsplit()`, `mx.TextTools.charsplit()`, `mx.TextT ools.splitat()`, `mx.TextTools.splitlines()` ? string.splitfields(...) 同`string.split()`. ? "".splitlines([keepends=0]) [string]模块里没有这个字符串对象方法。按行分割字符串。可选参数'keepends'确定 是否保留换行字符。

?

"".startswith(prefix [,start [,end]]) [string]模块里没有这个字符串对象方法。用来检查最前面几个字符是否和参数'suffix' 一样。后两个参数用来限定比较的起止位置。 参考: `"".endswith()`, `string.find()`

? ?

string.strip(s=...) "".strip() 删除前后的空白字符。Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> import string >>> s = """ ... Mary had a little lamb \011""" >>> string.strip(s) 'Mary had a little lamb' >>> s.strip() 'Mary had a little lamb' Python2.3+可以指定需要删除的字符。 >>> s = "MARY had a LITTLE lamb STEW" >>> s.strip("ABCDEFGHIJKLMNOPQRSTUVWXYZ") # strip caps ' had a LITTLE lamb ' 参考: `string.rstrip(), `string.lstrip()`

? ?

string.swapcase(s=...) "".swapcase() 转换大写为小写和小写为大写。 >>> import string >>> string.swapcase("mary HAD a little lamb!") 'MARY had A LITTLE LAMB!' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a Little Lamb!".swapcase() 'MARY had A LITTLE LAMB!' 参考: `string.upper()`, `string.lower()`

? ?

string.translate(s=..., table=... [,deletechars=""]) "".translate(table [,deletechars=""]) 根据字符表'table'转换字符串,参数'deletechars'指定需要删除的字符。 >>> import string >>> tab = string.maketrans('ABC','abc') >>> string.translate('MARY HAD a little LAMB', tab, 'Atl') 'MRY HD a ie LMb' Python1.6+,直接使用字符串对象的方法快一些。不过如果需要`string.maketrans()` 生成字符表的话还是要导入[string]模块。 >>> 'MARY HAD a little LAMB'.translate(tab, 'Atl') 'MRY HD a ie LMb' 使用`string.translate()`函数可以快速的修改字符串。 虽然需要设定字符表但速度远远超 过下面的程序:

>>> (new,frm,to,dlt) = ("",'ABC','abc','Alt') >>> for c in 'MARY HAD a little LAMB': ... if c not in dlt: ... pos = frm.find(c) ... if pos == -1: new += c ... else: new += to[pos] ... >>> new 'MRY HD a ie LMb' 参考: `string.maketrans()` ? ? string.upper(s=...) "".upper() 转换小写字母为大写。 >>> import string >>> string.upper("mary HAD a little lamb!") 'MARY HAD A LITTLE LAMB!' >>> string.upper("Mary had a Little Lamb!") 'MARY HAD A LITTLE LAMB!' Python1.6+,直接使用字符串对象的方法快一些。可以这样用: >>> "Mary had a Little Lamb!".upper() 'MARY HAD A LITTLE LAMB!' 参考: `string.lower()` ? string.zfill(s=..., width=...)

前补零。如果第一个字符是正负号则该正负号位于返回字符串的最前面。一般来说, `string.zfill()`是专用于数字字符串,但并没有作相应的检查。 >>> import string >>> string.zfill("this", 20) '0000000000000000this' >>> string.zfill("-37", 20) '-0000000000000000037' >>> string.zfill("+3.7", 20) '+00000000000000003.7' 参考: `string.rjust()` 取自"http://sunsol.wiki-site.com/index.php/Tpip_220"

? ?

?

?

1 第三节 -- 实践 2 练习 -- 筛选的多种方式 o 2.1 讨论: o 2.2 问题: 3 练习 -- 确保数据安全 o 3.1 讨论: o 3.2 问题: 4 练习 -- 搜索文件(全文索引) o 4.1 讨论: o 4.2 问题:

第三节 -- 实践 练习 -- 筛选的多种方式 讨论: 你是否还记得“Python 之道”里的那句“最佳途径只有一条”,和其他格言一样,真 实世界往往不会这么理想,不过这也不是什么坏事。 在'<comp.lang.python>'2001 年的帖子里就有一个简单问题的讨论。 问题内容是如何 识别电话号码。例如,'(123)456-7890','123-456-7890'或'123/456-7890'都代表着相 同的一个电话号码,这些输入形式在由用户自由输入的时候是非常常见的。在这里,最 典型的形式应该是'1234567890'。 把这个问题扩展成更为通用的情况:我们只是对文本中的部分字符感兴趣(在这里我们 感兴趣的是数字),其他都是一些没有用的东西。现在的问题就变成如何筛选提取需要 的内容。 最明显的做法就是通过循环来筛选。看起来就像这样: >>> s = '(123)/456-7890' >>> result = >>> for c in s: ... if c in '0123456789': ... result = result + c ... >>> result '1234567890' 这样也能完成工作,但看起来过于笨重,一个字符一个字符的操作好像也很奇怪。 一种简化的方法是使用正则表达式。如果你已经读过下一章,可能就会这样做: >>> import re >>> s = '(123)/456-7890' >>> re.sub(r'\D', , s) '1234567890' 不过对于这么一个小问题使用[re]模块的开销就有点过大了,而且效率也远远低于基本 字符串操作。 使用函数式风格是一种更简洁更有效的方法。例如: >>> s = '(123)/456-7890' >>> filter(lambda c:c.isdigit(), s) '1234567890'

还有一种方法,使用列表映射和字符串操作,以及期望能提高效率的 Python 字典: >>> isdigit = {'0':1,'1':1,'2':1,'3':1,'4':1, ... '5':1,'6':1,'7':1,'8':1,'9':1}.has_key >>> .join([x for x in s if isdigit(x)]) '1234567890' 问题: ? ? ? ? ? 1. 你通常会使用那种技术来解决这个问题?你更喜欢用哪种?请解释为什么? 2. 在这些不同技术上你觉得哪种效率较高?如果数据量很大的话, 这些技术需要有什么 样的变化? 3. 用实践来检验上题的回答。 4. 还有其他技术可以解决这个问题吗?效率如何?(提示:`string.translate()`) 5. 这些技术分别适合于哪些方面的应用?

练习 -- 确保数据安全 讨论: 前面已经提过 Python 标准库没有直接提供任何数字签名的功能。数字签名的主要作用 是检验和查证。当然不只是只有数字签名才能完成这样的任务。在密码学里一个经常讨 论的问题就是加密系统如何防止欺诈。 数据可能会被恶意篡改,但也可能是数据包丢失、存储介质错误或程序错误而造成的变 更。意外损坏是数据安全最简单直接的威胁。标准的技术是使用哈希验证码来保证数据 传输时的完整。接收数据时可以使用同样的算法计算数据的哈西特征值来做检验。这个 用起来非常简单: #---------- crc32.py ----------# # 计算输入内容的 CRC32 验证码 # 对大数据量采用增量读取的方式 # Usage: python crc32.py [file1 [file2 [...]]] # or: python crc32.py < STDIN import binascii import fileinput filelist = [] crc = binascii.crc32() for line in fileinput.input(): if fileinput.isfirstline(): if fileinput.isstdin(): filelist.append('STDIN') else: filelist.append(fileinput.filename()) crc = binascii.crc32(line,crc) print 'Files:', ' '.join(filelist) print 'CRC32:', crc 如果想要效率更快一些可以使用`zlib.adler32()`而不是`binascii.crc32`。CRC32 验证码 的重复率只有约 2**-32,足以用作一般的检验了。 但 CRC32 验证用于加密却很不安全。虽然随机数据误差几乎肯定不会造成杂凑碰撞, 但恶意篡改很容易就能找到同样的验证码数据。具体来说,假设真实信息是 M,很容易

就能找到假信息 M'使得 CRC32(M)等于 CRC32(M')。此外,假信息和真信息的区别并 不大,即使增强了一些额外验证条件也很难识别出真假。 为了防止欺诈信息,最好使用保密性更强的哈西加密技术,如[SHA]或[MD5]。操作方 法几乎和上面相同: #---------- sha.py ----------# # 计算输入内容的 SHA 哈西值 # Usage: python sha.py [file1 [file2 [...]]] # or: python sha.py < STDIN import sha, fileinput, os, sys filelist = [] sha = sha.sha() for line in fileinput.input(): if fileinput.isfirstline(): if fileinput.isstdin(): filelist.append('STDIN') else: filelist.append(fileinput.filename()) sha.update(line[:-1]+os.linesep) # same as binary read sys.stderr.write('Files: '+' '.join(filelist)+'\nSHA: ') print sha.hexdigest() SHA 或 MD5 哈希值基本上是无法伪造的,但是对于恶意欺诈,我们只靠哈希验证是不 够的。恶意欺诈同样也可以用 SHA 对虚假信息产生哈希校验码。像 CRC32 这样的校验 最常见的应用是保证数据本身不会损坏,通过连续的通道传输来保证数据不会损坏。例 如网页通常需要使用哈希验证来保证存放的文件数据。只使用连续通道并不能保证安 全,但它却很有效。 通过采用加密技术,可以保证传输通道的安全。这里的关键是加密数据的哈希值。特别 是对身份验证采用哈希校验。当然加密本身最好是用一个非对称加密算法,但通常情况 下使用对称加密也就足够了。例如,我们可以使用下面的这个工具: #---------- hash_rotor.py ----------# #!/usr/bin/env python # Encrypt hash on STDIN using sys.argv[1] as password import rotor, sys, binascii cipher = rotor.newrotor(sys.argv[1]) hexhash = sys.stdin.read()[:-1] # no newline print hexhash hash = binascii.unhexlify(hexhash) sys.stderr.write('Encryption: ') print binascii.hexlify(cipher.encrypt(hash)) 使用方法如下: #*-------- hash_rotor at work --------# % cat mary.txt Mary had a little lamb % python sha.py mary.txt | hash_rotor.py mypassword >> mary.txt Files: mary.txt SHA: Encryption: % cat mary.txt Mary had a little lamb c49bf9a7840f6c07ab00b164413d7958e0945941

63a9d3a2f4493d957397178354f21915cb36f8f8 倒数第二行是 SHA 哈希值,最后一行是最后加密的结果。使用的密码需要某种安全的 通道来传送。 问题: ? ? ? ? 1. 根据这些建议你要如何包装一个更强大的"digital_signatures.py"工具或模块?这 个工具可以解决哪些问题? 2. 为什么 CRC32 不适合加密?SHA 和 MD5 呢?(不需要知道具体算法就可以回答这 个问题)为什么算法的一致性比较重要? 3. 解释一下为什么需要哈希验证。 如果要防止黑客攻击, 你会怎么设计加密系统?你的 系统可以防止哪些攻击方式? 4. 使用哈希校验可以防止数据在传输时损坏。 但长度较短的哈希校验并不能做到绝对可 靠。如何提高这种校验系统的可靠性?如何定位损坏的数据点?如何尽可能的恢复损坏 的数据? 5. 高级:公共密匙算法实际上很简单,只涉及一些模运算和生成大质数。在下面的简介 里可以找到实现方法: [1] 尝试将公共密钥算法用在数字签名系统上。 练习 -- 搜索文件(全文索引) 讨论: 许多文本都是结构松散的,而不是有序的数据记录。对于这种文本,一个非常频繁的操 作就是查询某个东西是否包含在这个文本里--实际上更为常见的是搜索文件,你经常会 要在大量的文件中确定某一小部分是需要的内容。 查找文件并不属于文字处理的范畴。例如,要按特定时间和文件大小查找文件只需要使 用[os.path]模块就够了。以下是个例子,其中包括了一些典型的参数分析和帮助信息。 搜索文件本身的代码只有几行: #---------- findfile1.py ----------# # 按时间和大小找文件 _usage = """ Usage: python findfile1.py [-start=days_ago] [-end=days_ago] [-small=min_size] [-large=max_size] [pattern] Example: python findfile1.py -start=10 -end=5 -small=1000 -large=5000 *.txt """ import os.path import time import glob import sys def parseargs(args): """可用于不同平台的参数分析 选项开始标示可以是 - 或 / ,关键字后面可以是 = 或 : 。 没有对错误参数作检查

?

""" now = time.time() secs_in_day = 60*60*24 start = 0 # 时间范围 end = time.time() # 时间范围 small = 0 # 文件大小范围 large = sys.maxint # 文件大小范围 pat = '*' # 默认是对应所有文件 for arg in args: if arg[0] in '-/': if arg[1:6]=='start': start = now-(secs_in_day*int(arg[7:])) elif arg[1:4]=='end': end = now-(secs_in_day*int(arg[5:])) elif arg[1:6]=='small': small = int(arg[7:]) elif arg[1:6]=='large': large = int(arg[7:]) elif arg[1] in 'h?': print _usage else: pat = arg return (start,end,small,large,pat) if __name__ == '__main__': if len(sys.argv) > 1: (start,end,small,large,pat) = parseargs(sys.argv[1:]) for fname in glob.glob(pat): if not os.path.isfile(fname): continue # 不查找子目录 modtime = os.path.getmtime(fname) size = os.path.getsize(fname) if small <= size <= large and start <= modtime <= end: print time.ctime(modtime),'%8d '%size,fname else: print _usage 那如何在文件内容里寻找呢?使用`string.find()`就可以了。但是,对于多个文件来说命 中次数更为有用。在搜索结果中,根据命中次数来排序可能会更有帮助。下面的例子里 是按匹配比率来排序的: #---------- findfile2.py ----------# # Find files that contain a word _usage = "Usage: python findfile.py word" import os.path import glob import sys if len(sys.argv) == 2: search_word = sys.argv[1] results = [] for fname in glob.glob('*'): if os.path.isfile(fname): # 忽略子目录 text = open(fname).read() fsize = len(text) hits = text.count(search_word) density = (fsize > 0) and float(hits)/(fsize)

if density > 0: # 跳过未找到的 results.append((density,fname)) results.sort() results.reverse() print 'RANKING FILENAME' print '------- --------------------------' for match in results: print '%6d '%int(match[0]*1000000), match[1] else: print _usage 这里还可以有更多的变化。你可以在这个的基础上增加更为复杂的搜索和排序以及搜索 选项。例如,类似'grep'的功能等等。 像上面的搜索程序速度并不快,特别是文件数量多的时候。即使优化了速度也不一定能 令人满意。不过可以通过其他技术来提高速度。 一种通用的快速搜索技术就是定期创建索引--如数据库。以后的搜索并不是直接对内容 搜索,而是只检查结构化的索引。'indexer.py'就是一个很实用的例子。最新版本可从 本书的网站[2]上获得。 该工具'indexer.py'可以非常迅速的在多个文件里同时搜索多个关键字。例如,可以搜 索包含关键字'Python','index'和'search'的文件。如果文件数目太多,搜索特定组合可 能会比较慢。'indexer.py'使用的索引是字典。 完整的源代码'indexer.py'是值得一读的,但其中大部分是各种各样的储存机制和面向 对象编程(OOP)框架组合。但基本的实现很简单。主要有三个索引字典: #*---------- Index dictionaries ----------# *Indexer.fileids: fileid --> filename *Indexer.files: filename --> (fileid, wordcount) *Indexer.words: word --> {fileid1:occurs, fileid2:occurs, ...} '*Indexer.words'是最基本的,对每一单词都建立了包含哪些文件的索引字典。 '*Indexer.fileids'和'*Indexer.files'是辅助性索引。前者是使用数字代替文件名(提高性 能)。后者是每个文件对应的词频纪录,这在关联性排序时很重要。例如一个一兆字节 的文件中匹配命中 10 次的关联性就不如同样命中 10 次的一千字节文件。 创建和使用这些索引都很简单。要搜索多个关键字,只需要交叉查询就可以得到结果。 生成索引'*Indexer.words'也并不复杂。 问题: ? ? ? ? ? 1. 一个重要的概念的是如何区分一个词。 如何处理空白, 标点符号以及其他二进制数据 直接影响到分词的效果。特别是对于各种各样的文本,要如何做需要仔细的考虑。 2. 还有什么样的数据结构可以用于索引?效率如何?不同的结构都有哪些优点? 3. 如何发现原先的索引过的文件发生了变化?你会如何处理参照完整性?提示: 考虑使 用`binascii.crc32`,[sha]和[md5]来检验。 4. 如何区分文本文件和非文本文件? 5. 高级:索引信息的储存应该采取什么策略?请从速度和空间两方面考虑

取自"http://sunsol.wiki-site.com/index.php/Tpip_230"

前言
在生活、工作中,python 一直都是一个好帮手。在 python 的众多功能中,我觉 得文本处理是最常用的。下面是平常使用中的一些总结。环境是 python 3.3

0. 基础
在 python 中,使用 str 对象来保存字符串。str 对象的建立很简单,使用单引 号或双引号或 3 个单引号即可。例如:

s='nice'#output: nice s="nice"#output: nice s="Let's go"#output: Let's go s='"nice"'#output: "nice" s=str(1) #output: 1 s='''nice day'''#output: nice #output: day

在 python 中,\n 代表换行符,\t 代表 tab 键 在 python 中,引用 str 中的某一段的内容很容易。例如:

s='123456789' s[0] s[-1] s[:2] s[-2:] s[2:-2]

#第一个字符: output: 1 #倒数第一个字符: output: 9 #前 2 个字符: output: 12 #后 2 个字符: output: 89 #去掉前 2 个和后 2 个剩余的字符 output:34567

在 python 中,判断某一字符串是否在另一个字符串中:

'nice'in'nice day'#output :True

task 1.

按照某种格式生产字符串

在 python 中,str 对象有一个方法用于实现这种功能,这个方法是: str.format(*args,**kwargs)。例子:

'1+2={0}'.format(1+2) #{0}是占位符,其中 0 表示是第一个需要被替换的。 output: 1+2=3 '{0}:{1}'.format('nice','day') #{0},{1}是占位符,{0}指第一被替换,替 换成 nice,{1}第二个被替换,替换成 day。output:nice:day

实际用途: 我的手机拍照之后,手机的命名如下:

IMG_20130812_145732.jpg IMG_20130812_144559.jpg

在电脑中,会根据相片的日期放到不同的文件夹,文件夹命名如下:

2013-08-10 2013-08-12

所以说,要对相片的命名进行一个转换,这样才能映射到相应的文件夹转。代码 如下:

defgetName(name): return'{0}-{1}-{2}'.format(name[4:8],name[8:10],name[10:12]) getName('IMG_20130812_145732.jpg') #output: 2013-08-12

task 2. 替换字符串中的某一部分
替换有 2 中方法,一种是使用 str 对象自带的方法 replace(),另一种是使用 re 模块中 sub(0 的。例如:

#replace s='nice day'

s.replace('nice','good') good day

#s 本身不改变,但会返回一个字符串:output:

#sub import re s='cat1 cat2 cat3 in the xxx' re.sub('cat[0-9]','CAT',s) #s 本身不改变,但会返回一个字符串:output: CAT CATCAT in the xxx

对于 re 模块中的 sub,需要了解正则表达式。

task 3. 拆分字符串
Excel 可以到处逗号分隔符格式的文件。对于这样的字符串,我们可以把它拆成 相应的字段。实现这个功能,主要使用 str 对象的自带方法 split。例如:

s='one,two,three' s.split(',') 'three']

#output: ['one', 'two',

task 4. 合并字符串
除了拆分功能之外, 我们可以将拆分好的字段合并成一个字符串。 实现这个功能, 主要使用 str 对象自带的方法 join。例如:

l=['one', 'two', 'three'] ','.join(l)

#output: one,two,three

这个功能还可以在 this 模块中看到。

task 5. 整合
关于字符串的操作有很多。 如果仅仅对一两行字符串进行操作,显示不出他的威 力。 在工作中, 有可能会对文档进行处理, 有的文档很大, 手工的方式不好处理, 这时,python 就有用武之地。 例如,从数据库中导出一个表 table_1 的数据,导出来的数据格式如下:

insertinto table_1(field1,filed2,field3) values(value1,value2,value3); ... insertinto table_1(field1,filed2,field3) values(value1,value2,value3);

数据生成的文件的大小大概为 700M。要把这个表的数据导入到另一个数据库的 表 table_2 中,table_1 和 table_2 的表结构相同,仅仅是名字不同。这时,我 们可以写一个 python 脚本,将 table_1 替换成 table_2。例如:

path_in='table1.data' path_out='table2.data' f_in=open(path_in) f_out=open(path_out,'w') for i inf_in.readlines(): if'insert into table_1(field1,filed2,field3)'in i: f_out.write(i.repalce('tabel_1','table_2')) else: f_out.write(i) f_in.close() f_out.close()

结束语
使用 python,让日常工作多了一个工具,多了一个选择。可以将一些重复的工 作交给机器做,节省时间,提高效率。


更多相关文档:

用Python作文本处理_第一章_Python基础

Python 作文本处理/第一章 目录 ? 1 第一章 -- Python 基础 o 1.1 第一节 -- 技巧和形式 1.1.1 主题 -- 在文字处理里使用高阶函数 1.1.1.1 ...

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

Python作文本处理 第二章 -- 基本的字符串运用_IT/计算机_专业资料。Text Processing In Python(用Python作文本处理)第二章中文翻译用...

用Python做文本处理 第一章

用Python做文本处理 第一章_计算机软件及应用_IT/计算机_专业资料。用 Python 作文本处理/第一章 目录 ? 1 第一章 -- Python 基础 o 1.1 第一节 -- 技...

Python2.7处理文本常用代码模块

() #从文件中读取全部行 #对每一行数据进行处理 #打开文件,模式为附加 #将数据写入文件附加在最后 【Python 模块】for 循环示例连加 sum=0 for a in range(...

字作文大全题目是

? ? ? ? 2009-07-16 小学生作文大全 有作文题目是( )变了 1465 2010-09...1 回答 mac 虚拟机玩游戏不能存档 0 回答 5 关于 BT5 R3 Python 和 W3AF ...

Python的中文处理

Python的中文处理_IT/计算机_专业资料。Python的中文处理 的中文处理一、使用中文字符 在python源码中如果使用了中文字符,运行时会有错误,解决的办法是在源码的开头...

python——常用功能之文本处理

python——常用功能之文本处理_计算机软件及应用_IT/计算机_专业资料。前言在生活...用Python做文本处理 第一... 33页 免费 用Python作文本处理 第二... 30页...

用Python来完成简单图像处理

Python 来完成简单图像处理 摘要: 摘要:本文简单介绍了用 Python 来完成简单图像处理任务的方法。虽然图像处理的最佳工 具是 MATLAB 的图像处理工具箱,但是在进行...

Python中文处理

Python中文处理_计算机软件及应用_IT/计算机_专业资料。python 处理中文文件,必须要转码,才能用于正则 Python 中文处理 获取当前 python 系统编码 Import os Sys.get...

用Python进行自然语言处理——图文x详解(一)

Python 进行自然语言处理(一) 2014.07.27 语言计算:文本和单词,NLTK 入门首先应该安装 NLTK,可以从官网免费下载,按照说明下载适合你的操作系统的版本。 安装完 ...
更多相关标签:
python 异常处理 | python 图像处理 | python自然语言处理 | python 文本处理 | python 字符串处理 | python 图片处理 | python 处理excel | python 文件处理 |
网站地图

文档资料共享网 nexoncn.com copyright ©right 2010-2020。
文档资料共享网内容来自网络,如有侵犯请联系客服。email:zhit325@126.com