python.cookbook读书笔记之文本篇(一)

前言:

在一行的末尾使用反斜杠符号意味着下面一行仍是上面字符串的延续

Big = "This is a long string\
That prints two line."

使用一对连续的三引用符将字符串圈起:

Bigger = """

This is an even

bigger string that

Spans thress lines.

"""

使用这种符号文本将按照原貌被存储在Python的字符串对象中

在字符串前面加一个r或者R,表示该字符串是一个真正的”原”字符串

Big =r "This is a long string\

That prints two line."

使用了r或者R,即使字符串中又反斜杠,起转义也会被完全忽略,就是一个普通的反斜杠字符

mystr = "my string"

mystr[-2]  #'n'

mystr[:3:-1] #'gnirt'

如果一个庞大的包含多行文本的字符串,可以用splitlines来将其分隔为多个单行字符串并置入一个列表中:

list+of_lines = one_large_string.splitlines()

然后还可以用join来重新生成一个庞大的单个字符串:

one_large_string = '\n'.join(list_of_lines)

 

    1. 每次处理一个字符串

使用内建的list(list(‘abcde’) = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’])

thelist = list(thestring)

列表推导

result = [do_something_with(c) for c in thestring]

使用内建的map函数

results = map(do_something, thestring)

map() 会根据提供的函数对指定序列做映射。

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

>>>def square(x) : # 计算平方数 

... return x ** 2

 ... >>> map(square, [1,2,3,4,5]) # 计算列表各个元素的平方

 [1, 4, 9, 16, 25] 

>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数 

[1, 4, 9, 16, 25] # 提供了两个列表,对相同位置的列表数据进行相加 

>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]) 

[3, 7, 11, 15, 19]

 

1.2 字符和字符值之间的转换

ord和chr的应用

>>> print ord(a)

97

>>> print chr(97)

a

ord适用于普通字符和unicode,但是假如值转换为unicode字符需要unichr

>>> print repr(unichr(8224))

u\u2020

注:repr() 函数将对象转化为供解释器读取的形式。

>>>s = 'RUNOOB'

>>> repr(s) 

"'RUNOOB'"

把一个字符串转换为包含各个字符的值的列表

>>> print map(ord, ciao)

[99, 105, 97, 111]

若想反过来利用一个包含字符值的列表创建字符串,可以使用.join(列表变串用join)

>>> print ‘’.join(map(chr, range(97, 100)))

abc

 

1.3 测试一个字符串是否是类字符串(或者具有字符串的行为)

def isAString(anobj):

    return isinstance(anobj, basestring)

也有人可能会用

def isExactlyAString(anobj):

    return type(anobj) is type('')

但是使用type很糟糕,对于多态性完全不适用,自己编写的str的子类不行,任何行为表现类似字符串的也不能通过测试,使用isinstance则不同,任何继承自basestring(str, unicode都继承自basestring)的子类都可以通过测试。

或者运用’走路像鸭子,叫声也像鸭子,我们就可以认为它是鸭子’理论来实现定义:

defe isStringLike(anobj):

    try: anobj.lower() + anobj + ''

    except: return False

    else: return True

实现此方法是因为isAString只适用于继承自basestring的子类,其他类似字符串的类型则不适用,因此实现isStringLike去判定一些符合字符串类型行为的类就是类字符串

 

1.4 字符串对齐

对齐方式有三种:左对齐,右对齐,居中,对应的方法分别是ljust、rjust、center

>>> print ‘|’, ‘hej’.ljust(20), ‘|’, ‘hej’.rjust(20), ‘|’, ‘hej’.center(20), ‘|’

| hej        |        hej |    hej     |

 

>>> print ‘hej’.center(20, ‘+’)

++++++++hej+++++++++

 

1.5 去除字符串两端特定的字符(lstrip、rstrip、strip)

>>> x = ‘xyxxyy hejyx yyx’

>>> print ‘|’ + x.strip(‘xy’) + ‘|’

寓意:去除字符串两端连续的x或y字符直到不是x或y位置(上述例子遇到空格即停止去除)

 

1.6 合并字符串(推荐使用.join而不是+=)

使用 += 拼接字符串示例:

s = ‘foo’

s += ‘bar’

s += ‘baz’

首先s存放foo申请一次内存,然后当s += ‘bar’时,由于字符串对象的不可变特性,所以需要重新申请内存存放’foo’和’bar’组成的’foobar’(此时相当于’foo’所需的内存被申请2次、bar所需的内存被申请1次),当再次s += ‘baz’时,则又需要重新申请内存存放’foobar’和’baz’组成的’foobarbaz’(此时相当于’foo’所需的内存被申请3次、bar所需的内存被申请2次、baz所需的内存被申请1次)。最终拼接N个字符串将涉及创建并丢弃N-1个中间结果(不必要的内存)

那么对于三个字符串的拼接消耗的分配内存的次数相当于’foo’会申请3次、bar会申请2次

、baz会申请1次.延申出去N个字符串的拼接,第一个字符串会被申请N次,最后一个字符会被申请1次,累加就是N*(N+1)/2。即所花的使劲按跟需要累加的字符数的平方成正比。

 

tp = [‘foo’, ’bar’, ‘baz’]

s = ‘’.join(tp)

但是join不一样,起不会多次申请空间,会一次性预先计算出整个拼接字符串所需要的空间,最后把每个字符串复制到新的空间,所以每个字符串仅需要复制一次。(少量的话建议用字符串格式化符%)

 

对比一下性能:

# -*- coding: UTF-8 -*-

from time import time

def plus_string():

    curTime = time()

    s = ''

    for i in range(1000000):

        s += 'test'

    print(time() - curTime)



def join_string():

    curTime = time()

    s_list = []

    for i in range(1000000):

        s_list.append('test')

    s = ''.join(s_list)

    print(time() - curTime)

 

0.6941249370574951

0.06981253623962402

可以看到差得不是一星半点

 

1.7 将字符串逐字符或逐词反转

(1)、逐字符反转 revchars = astring[::-1]

(2)、逐词反转 revchars =  .join(astring.split()[::-1])

(3)、逐词反转,但是保留原字符串得空格且同时反转空格

import re

revwords = re.split(r'(\s+)', astring)   #切割字符串为单词列表

revwords.reverse()                  #反转列表

revwords = ''.join(revwords)         #单词列表->字符串

 

1.8 检查字符串中是否包含某字符集合中得字符

(1)、通用方法(适用于任何可迭代得容器)

def containsAny(seq, aset):

“”“检查序列seq是否含有aset中的项”“”

for c in seq:

    if c in aset: return True

    return False

import itertools

def containsAny(seq, aset):

    for item in itertools.ifilter(aset.__contains__, seq):

        return True

    return False

注:第二个版本基于标准库itertools模块的方法,itertools.ifilter要求传入一个断定和一个可迭代对象,然后筛选可迭代对象中满足断定的所有子项。这里断定用的时aset.__contains__,其就是in aset的内部调用(if some in aset内部调用的就是aset.__contains__)。

  1. 、如果只是处理seq和aset中的字符串,可以结合字符串的方法translate和Python标准库中的string.maketrans函数:

i

import string

notrans = string.maketrans('', '')   #iidentity "translation"

def containsAny(astr, strset):

    return len(strlset) != len(strset.translate(notrans, astr))

def containAll(astr, strset):

    return not strset.translate(notrans, astr)

这里注意两个知识点:string.maketrans和str.translate方法

maketrans() 方法用于创建字符映射的转换表(python2当中返回转变之后的256个字符的字符串,python3中返回dict),对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。

#!/usr/bin/python# -*- coding: UTF-8 -*-

from string import maketrans   # 必须调用 maketrans 函数。

intab = "aeiou"

outtab = "12345"

trantab = maketrans(intab, outtab)

str = "this is string example....wow!!!";print str.translate(trantab);

th3s 3s str3ng 2x1mpl2....w4w!!!

translate() 方法根据参数table给出的表(包含 256 个字符)转换字符串的字符,要过滤掉的字符放到 deletechars 参数中

#!/usr/bin/python 

# 制作翻译表 

bytes_tabtrans = bytes.maketrans(b'abcdefghijklmnopqrstuvwxyz', b'ABCDEFGHIJKLMNOPQRSTUVWXYZ') 

# 转换为大写,并删除字母o 

print(b'runoob'.translate(bytes_tabtrans, b'o'))

b'RUNB'

所以string.maketrans(‘’, ‘’)实际上什么也不作,只是作一个转换表用于strset.translate(notrans, astr)的第一个参数,strset.translate(notrans, astr)真正的意义在于筛选strset当中和astr一样的字符。

若是没有,则strset不会删除任何字符,那么其长度就不会改变,所以return len(strset) != len(strset.translate(notrans, astr))就表明strset当中存在和astr一样的字符(发生删除事件,长度较之前会改变)。同样的,若删除光了,则表示strset中所有的字符都来自于astr,那么strset.translate(notrans, astr)会等于0.所以containsAll方法用return not strset.translate(notrans, astr)实现。

 

1.9 简化字符串的translate方法的使用(由于python2和3的maketrans的差异性,此方法只适用于python2)

# -*- coding: UTF-8 -*-

import string

def translator(frm='', to='', delete='', keep=None):

    if len(to) == 1:

        to = to * len(frm)

    trans = string.maketrans(frm, to) #返回一个包含frm已经转换为to的256个字符组成的字符串

    if keep is not None:

        allchars = string.maketrans('', '') #返回一个无变化的256个字符组成的字符串(普通的字符只能打印256种)

        delete = allchars.translate(allchars, keep.translate(allchars, delete)) #返回不包含keep中字符的字符串

    def translate(s):

        return s.translate(trans, delete) #返回s当中不包含delete中字符的字符串

    return translate

上述实现是一个返回闭包的工厂函数,可以大大提高通用性

>>> digits_only = translator(keep=string.digits)

>>> digits_only(Chris Perkins : 224-7992)

2247992

>>> digits_only = translator(delete=string.digits)

>>> digits_only(Chris Perkins : 224-7992)

Chris Perkins : -

>>> digits_only = translator(from=string.digits, to=#)

>>> digits_only(Chris Perkins : 224-7992)

Chris Perkins : ###-####

当然其也有一些无端,当同时存在delete和keep的时候优先处理delete再处理keep

>>> digits_only = translator(delete=abcd, keep=cdef)

>>> digits_only(abcdefg)

ef

当然也可根据不同的需要去改变实现处理delete和keep的优先级

1.10 过滤字符串中不属于制定集合的字符

这个肯定是直接联想到1.9所使用的translate,做一个闭包函数(实际上1.9的闭包函数直接可以使用)

# -*- coding: UTF-8 -*-

import string

allchars = string.maketrans('', '')

def makefilter(keep):

    #删除要保存的keep

    delchars = allchars.translate(allchars, keep)

    def thefilter(s):

        #删除除keep之外的delchars,得到的就是待保留的keep包含的字符

        return s.translate(allchars, delchars)

    return thefilter



if __name__ == '__main__':

    just_vowels = makefilter('aeiouy')

    print just_vowels('four score and seven years ago')  #ouoeaeeyeaao

    print just_vowels('tiger, tiger burning bright')  #ieieuii

 

这个适用于非unicode得普通字符串,但是对于带unicode得字符串,要达到此效果,接口还有些差异,unicode得tanslate方法只接受一个参数:一个序列或者映射,其是根据字符串中每个字符相应得unicode码值进行索引。假如索引(unicode码值)没有找到对应的索引值(或者映射的键),那么什么也不做,还是原unicode码对应的字符。与每个字符unicode码值对应值必须是一个Unicode字符串(该字符得替换物)或者(None)。

假如直接用函数实现,我们的参数必须提供一个非常庞大的dict或string--但仅仅是把所有的其他字符映射到None。更好的办法是实现一个提供__getitem__方法的类,此方法在列表索引会调用。

# -*- coding: UTF-8 -*-

import sets

class Keeper(object):

    def __init__(self, keep):

        self.keep = sets.Set(map(ord, keep))



    def __getitem__(self, n):

        if n not in self.keep:

            return None

        return unichr(n)



    def __call__(self, s):

        return unicode(s).translate(self)

makefilters = Keeper

if __name__ == '__main__':

    just_vowels = makefilters('aeiouy')

    print just_vowels(u'four score and seven years ago') #ouoeaeeyeaao

 

1.11 检查一个字符串是文本还是二进制f

from __future__ import division  #超前使用python 3函数,防止只能整除,此模块可以确保精确的结果

text_characters = "".join(map(chr, range(32, 127))) + "\n\r\t\b"

_null_trans = string.maketrans("", "")

def istext(s, text_characters=text_characters, threshold=0.3)

    # s包含空值,不是文本

    if '\0' in s:

        return False



    #一个"空"字符串是"文本"

    if not s:

        return True

    t = s.translate(_null_trans, text_characters)

    return len(t) / len(s) <= threshold



#用于判断文件

def istextfile(filename, blocksize=512, **kwds):

    return istext(open(filename).read(blocksize), **kwds)

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值