《Python Cookbook(第2版)中文版》——1.13 访问子字符串

1.13 访问子字符串

任务

获取字符串的某个部分。比如,你读取了一条定长的记录,但只想获取这条记录中的某些字段的数据。

解决方案

切片是个好方法,但是它一次只能取得一个字段:

afield = theline[3:8]

如果还需考虑字段的长度,struct.unpack可能更适合。比如:

import struct
# 得到一个5字节的字符串,跳过3字节,得到两个8字节字符串,以及其余部分:
baseformat = "5s 3x 8s 8s"
# theline超出的长度也由这个base-format确定
#(在本例中是24字节,但struct.calcsize是很通用的)
numremain = len(theline) - struct.calcsize(baseformat)
# 用合适的s或x字段完成格式,然后unpack
format = "%s %ds" % (baseformat, numremain)
l, s1, s2, t = struct.unpack(format, theline)

如果想跳过“其余部分”,只需要给出正确的长度,拆解出theline的开头部分的数据即可:

l, s1, s2 = struct.unpack(baseformat, theline[:struct.calcsize(baseformat)])

如果需要获取5字节一组的数据,可以利用带列表推导(LC)的切片方法,代码很简单:

fivers = [theline[k:k+5] for k in xrange(0, len(theline), 5)]

将字符切成一个个单独的字符更加容易:

chars = list(theline)

如果想把数据切成指定长度的列,用带LC的切片方法通常是最简单的:

cuts = [8, 14, 20, 26, 30]
pieces = [ theline[i:j] for i, j in zip([0]+cuts, cuts+[None]) ]

在LC中调用zip,返回的是一个列表,其中每项都是形如(cuts[k], cuts[k+1])这样的数对,除了第一项和最后一项,这两项分别是(0, cuts[0])和(cuts[len(cuts)-1], None)。换句话说,每一个数对都给出了用于切割的正确的(i, j),仅有第一项和最后一项例外,前者给出的是切割之前的切片方式,后者给出的是切割完成之后到字符串末尾的剩余部分。LC利用这些数对就可以正确地将theline切分开来。

讨论

本节受到了Perl Cookbook 1.1的启发。Python的切片方法,取代了Perl的substr。Perl的内建的unpack和Python的struct.unpack也非常相似。不过Perl的手段更丰富一点,它可以用*来指定最后一个字段长度,并指代剩余部分。在Python中,无论是为了获取或者跳过某些数据,我们都得计算和插入正确的长度。不过这不是什么大问题,因为此类抽取字段数据的任务往往可以被封装成小函数。如果该函数需要反复被调用的话,memoizing,通常也被称为自动缓存机制,能够极大地提高性能,因为它避免了为struct.unpack反复做一些格式准备工作。参见第18.5节中关于memoizing的更多细节。

在纯Python的环境中,struct.unpack作为字符串切片的一种替代方案,非常好用(当然不能和Perl的substr比,虽然它不接受用*指定的区域长度,但仍是值得推荐的好东西)。

这些代码片段,最好被封装成函数。封装的一个优点是,我们不需要每次使用时都计算最后一个区域的长度。下面的函数基本上等价于“解决方案”小节给出的直接使用struct.unpack的代码片段:

def fields(baseformat, theline, lastfield=False):
      # theline超出的长度也由这个base-format确定
      #(通过struct.calcsize计算确切的长度)
      numremain = len(theline)-struct.calcsize(baseformat)
      # 用合适的s或x字段完成格式,然后unpack
      format = "%s %d%s" % (baseformat, numremain, lastfield and "s" or "x")
      return struct.unpack(format, theline)

一个值得注意(或者说值得批评)的设计是该函数提供了lastfield=False这样一个可选参数。这基于一个经验,虽然我们常常需要跳过最后的长度未知的部分,有时候我们还是需要获取那段数据。采用lastfield and s or x(等同于C语言中的三元运算符,lastfield?"s":"c")这样的表达式,我们省去了一个if/else,不过是否需要为这点紧凑牺牲可读性还有值得商榷之处。参看第18.9节中有关在Python中模拟三元运算符的内容。

若fields函数在一个循环内部被调用,使用元组(baseformat, len(theline), lastfield)作为key来充分利用memoizing机制将极大地提高性能。这里给出一个使用memoizing机制的fields版本:

def fields(baseformat, theline, lastfield=False, _cache={  }):
      # 生成键并尝试获得缓存的格式字符串
      key = baseformat, len(theline), lastfield
      format = _cache.get(key)
      if format is None:
            # 没有缓存的格式字符串,创建并缓存之
            numremain = len(theline)-struct.calcsize(baseformat)
            _cache[key] = format = "%s %d%s" % (
            baseformat, numremain, lastfield and "s" or "x")
      return struct.unpack(format, theline)

这种利用缓存的方法,目的是将比较耗时的格式准备工作一次完成,并存储在_cache字典中。当然,正像所有的优化措施一样,这种采用了缓存机制的优化也需要通过测试来确定究竟能在多大程度上提高性能。对这个例子,我的测试结果是,通过缓存优化的版本要比优化之前快约30%到40%,换句话说,如果这个函数不是你的程序的性能瓶颈部分,其实没有什么必要多此一举。

“解决方案中”给出的另一个关于 LC的代码片段,也可以封装成函数:

def split_by(theline, n, lastfield=False):
      # 切割所有需要的片段
      pieces = [theline[k:k+n] for k in xrange(0, len(theline), n)]
      # 若最后一段太短或不需要,丢弃之
      if not lastfield and len(pieces[-1]) < n:
            pieces.pop( )
      return pieces

对最后一个代码片段的封装:

def split_at(theline, cuts, lastfield=False):
      #切割所有需要的片段
      pieces = [ theline[i:j] for i j in zip([0]+cuts, cuts+[None]) ]
      # 若不需要最后一段,丢弃之
      if not lastfield:
            pieces.pop( )
      return pieces

在上面这些例子中,利用列表推导来切片要比用struct.unpack略好一些。

用生成器可以实现一个完全不同的方式,像这样:

def split_at(the_line, cuts, lastfield=False):
      last = 0
      for cut in cuts:
            yield the_line[last:cut]
            last = cut
      if lastfield:
            yield the_line[last:]
def split_by(the_line, n, lastfield=False):
      return split_at(the_line, xrange(n, len(the_line), n), lastfield)

当需要循环遍历获取的结果序列时,无论是显式调用,还是借助一些可调用的“累加器”,比如’’.join来进行隐式调用,基于生成器的方式都会更加合适。如果需要的是各字段数据的列表,你手上得到的结果却是一个生成器,可以调用内建的list来完成转化,像这样:

list_of_fields = list(split_by(the_line, 5))
时间: 2024-10-23 21:49:07

《Python Cookbook(第2版)中文版》——1.13 访问子字符串的相关文章

《“笨办法”学Python(第3版)》——习题6 字符串和文本

习题6 字符串和文本 虽然你已经在程序中写过字符串了,但是你还不了解它们的用处.在这个习题中我们将使用复杂的字符串来建立一系列变量,从中你将学到它们的用途.首先,我们解释一下字符串是什么. 字符串通常是指你想要展示给别人的或者是想要从程序里"导出"的一小段字符.Python可以通过文本里的双引号(")或者单引号(')识别出字符串来.这在以前的打印练习中你已经见过很多次了.如果你把单引号或者双引号括起来的文本放到print后面,它们就会被Python打印出来. 字符串可以包含之

《Python Cookbook(第2版)中文版》——导读

前 言 这本书不是一本典型的O'Reilly风格的书,而是一本集合了多个作者的手稿的作品.实际上,这也是一种将开源开发的方式应用到书籍出版业的尝试.Python社区有超过300个成员在本书中贡献了他们的心得和资料.在这里,我们作为编辑,想给你--本书的读者,介绍一些重要的背景资料,这些背景资料是关于此书是如何编著出来,以及这个过程和涉及的人,并提出一些关于这种崭新的风格的思考. 目 录 [第1章 文本1.1 每次处理一个字符](https://yq.aliyun.com/articles/963

《Python Cookbook(第2版)中文版》——第1章 文本 1.1 每次处理一个字符

第1章 文本 引言 感谢:Fred L. Drake, Jr.,PythonLabs 对于脚本语言来说,文本处理任务构成了一个重要的组成部分,每个人都会同意文本处理非常有用.每个人都会有一些文本需要重新格式化或者转化为另一种形式.问题是,每个程序都与另一个程序有点不同,无论它们是多么相似,想提取出一些可复用的代码片段并用它来处理不同的文件格式仍然是非常困难的. 什么是文本 看起来问题有点简单得过分了,事实上,我们看到了文本,就知道了什么是文本,文本是一串字符,这正是它与二进制数据之间的不同.二进

《Python Cookbook(第2版)中文版》——1.10 过滤字符串中不属于指定集合的字符

1.10 过滤字符串中不属于指定集合的字符 任务 给定一个需要保留的字符的集合,构建一个过滤函数,并可将其应用于任何字符串s,函数返回一个s的拷贝,该拷贝只包含指定字符集合中的元素. 解决方案 对于此类问题,string对象的translate方法是又快又好用的工具.不过,为了有效地使用translate来解决问题,事先我们必须做一些准备工作.传递给translate的第一个参数是一个翻译表:在本节中,我们其实不需要什么翻译,所以我们必须准备一个特制的参数来指明"无须翻译".第二个参数

《Python Cookbook(第2版)中文版》——1.16 替换字符串中的子串

1.16 替换字符串中的子串 任务 需要一个简单的方法来完成这样一个任务:给定一个字符串,通过查询一个替换字典,将字符串中被标记的子字符串替换掉. 解决方案 下面给出的解决办法既适用于Python 2.3,也适用于2.4: def expand(format, d, marker='"', safe=False): if safe: def lookup(w): return d.get(w, w.join(marker*2)) else: def lookup(w): return d[w]

《Python Cookbook(第2版)中文版》——1.17 替换字符串中的子串—Python 2.4

1.17 替换字符串中的子串-Python 2.4 任务 在Python 2.4的环境下,你想完成这样的任务:给定一个字符串,通过查询一个字符串替换字典,将字符串中被标记的子字符串替换掉. 解决方案 Python 2.4提供了一个新的string.Template类,可以应用于这个任务.下面给出一段代码以展示怎样使用这个类: import string # 从字符串生成模板,其中标识符被$标记 new_style = string.Template('this is $thing') # 给模板

拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录

目录 目录 关于这本书 必看知识点 最后 关于这本书 <Python 核心编程 (第二版)>是一本 Python 编程的入门书,分为 Python 核心(其实并不核心,应该叫基础) 和 高级主题 两大部分,以 Python 2.x 作为主要演示版本,涵盖的知识面广,知识点较齐全,代码多且好理解,但对 Python 版本特性的内容太久远,不合时宜. 整体来说 Python 核心 部分是主要内容,高级主题 部分作为应用扩展内容.后半部分篇幅较短,内容不够深入,只到了解的层面,好在横向够广(每一个主

《Python 3程序开发指南(第2版•修订版)》——2.4 字符串

2.4 字符串 字符串是使用固定不变的str数据类型表示的,其中存放Unicode字符序列.str数据类型可以作为函数进行调用,用于创建字符串对象--参数为空时返回一个空字符串,参数为非字符串类型时返回该参数的字符串形式,参数为字符串时返回该字符串的拷贝.str()函数也可以用作一个转换函数,此时要求第一个参数为字符串或可以转换为字符串的其他数据类型,其后跟随至多两个可选的字符串参数,其中一个用于指定要使用的编码格式,另一个用于指定如何处理编码错误. 前面我们注意到,字符串是使用引号创建的,可以

谁有&amp;amp;lt;&amp;amp;lt;CLR Via C#&amp;amp;gt;&amp;amp;gt;第三版中文版的电子书

问题描述 谁有<<CLRViaC#>>第三版中文版的电子书,我是个初学者,看网上推荐此书的人多,想看一下,我的QQ:330784617.谢谢!! 解决方案 解决方案二:试一试我一般看英文的,虽然很少看书:(解决方案三: 解决方案四:第二版有的,想看第三版.