Python内置数据结构之字符串

字符串 今天跟大家来说一说Python中的字符串数据结构。

上文回顾

让我们回顾一下可变类型及不可变类型:

  • 不可变数据类型:strinttuple
  • 可变数据类型:dictlist

今天讲解的字符串属于不可变类型。

Python字符串编码

Python3中的字符串是Unicode的序列,也就是说,Python3的字符串支持多语言了;Python2中的字符串是byte序列。

例如:

In[1]: print('含有中文的字符串str')
含有中文的字符串str

对于单个字符的编码,Python提供了ord()内置函数来获取字符的整数表示;chr()内置函数则把编码转换为对应的字符。例如:

In[2]: ord('A')
Out[2]: 65

In[3]: ord('中')
Out[3]: 20013

In[4]: chr(97)
Out[4]: 'a'

In[5]: chr(25991)
Out[5]: '文'

如果要知道字符的整数编码,那么还可以用其十六进制的形式这么写:

In[6]: '\u4e2d\u6587'
Out[6]: '中文'

Python的字符编码就介绍到这里。接下来介绍Python字符串的常用方法,看看字符串在日常当中是怎么用的。

字符串常用方法

字符串常用方法:

  1. 字符串连接:join
  2. 字符串分割:splitrsplitsplitlinespartitionrpartition
  3. 字符串修改-大小写:capitalizetitlelowerupperswapcase
  4. 字符串修改-填充或清除:centerljustrjustzfillstriprstriplstrip
  5. 字符串查找替换:countfindrfindindexrindexreplace

字符串连接(join)

join允许我们使用特定的字符来连接字符串。直接看例子吧:

In[7]: lst = ['i', 'am', 'lavenliu']

In[8]: ' '.join(lst)
Out[8]: 'i am lavenliu'

join是字符串方法,参数是内容为字符串的可迭代对象,接收者(在这里为空格)作为连接符。使用逗号进行连接:

In[9]: ','.join(lst)
Out[9]: 'i,am,lavenliu'

除了join外,我们还可以使用+进行两个字符串的连接:

In[10]: 'my' + ' name'
Out[10]: 'my name'

字符串分割(split系列方法)

# split
>>> s = 'my name is lavenliu'
>>> s
'my name is lavenliu'
>>> s.split()
['my', 'name', 'is', 'lavenliu']
>>> s.split(maxsplit=1) # maxsplit参数表示分割多少次;默认值为-1,表示分割所有分隔符
>>> s.split('ls')
['my name is lavenliu']
>>> s.split('is')
['my name ', ' lavenliu']
>>> s.split(' ', 1)
['my', 'name is lavenliu']
s.split('is')   # 以“is”为分隔符
s.split(' ', 1) # 以空格为分隔符,从左到右分隔一次
s.split(' ', 2) # 以空格为分隔符,从左到右分隔两次
s.split(' ', -1) # -1就是默认值,直到字符串分隔完成
['my', 'name', 'is', 'lavenliu']

# rsplit方法
s.rsplit()
['my', 'name', 'is', 'lavenliu']

s.rsplit(' ')
['my', 'name', 'is', 'lavenliu']

s.rsplit(' ', 1) # 与s.split(' ', 1)的分隔形式相反
['my name is', 'lavenliu']

我们看一看split函数的原型该怎么写:

def split(s, sep, maxsplit):
    ret = []
    tmp = []
    i = 0
    for c in s:
        if c != sep:
            tmp.append(c)
        else:
            i += 1
            ret.append(''.join(tmp))
            tmp.clear()
        if maxsplit > 0 and i >= maxsplit:
            return ret
    return ret

rsplit方法的原型为:

def rsplit(s, sep, maxsplit):
    ret = []
    tmp = []
    for c in reversed(s):
        if c != sep:
            tmp.append(c)
        else:
            i += 1
            ret.append(''.join(reversed(tmp)))
            tmp.clear()
        if maxsplit > 0 and i >= maxsplit:
            ret.append()
            return reversed(ret)
    return reversed(ret)

splitlines方法:

In[12]: s = '''i am lavenliu
    ...: i love python'''

In[13]: print(s.splitlines())     # 按行分割,并且返回结果不带换行符
['i am lavenliu', 'i love python']

In[14]: print(s.splitlines(True)) # 按行分割,并且返回结果带换行符
['i am lavenliu\n', 'i love python']

partition方法:

In[15]: s = 'i am lavenliu'

In[16]: s.partition(' ')
Out[16]: ('i', ' ', 'am lavenliu')

partition总是返回一个三元组,它按传入的分隔符分割一次,得到head,tail,返回结果是head,sep,tail。rpartition是partition从右往左的版本。再看一个partition的例子,

In[17]: cfg = 'mysql.connect = mysql://user:password@127.0.0.1:3306/test'

In[18]: print(cfg.partition('='))
('mysql.connect ', '=', ' mysql://user:password@127.0.0.1:3306/test')

In[19]: cfg = 'env = PATH=/usr/bin:$PATH'

In[20]: print(cfg.partition('='))
('env ', '=', ' PATH=/usr/bin:$PATH')

In[21]: print(''.partition('='))
('', '', '')

In[22]: print('='.partition('='))
('', '=', '')

partition方法实现:

def partition(s, sep):
    if s == '':
        return '', '', ''
    tmp = s.split(sep, maxsplit=1)
    if len(tmp) == 2:
        return tmp[0], sep, tmp[1]
    if len(tmp) == 1:
        return tmp[0], sep, ''

字符串大小写转换:

In[23]: s = 'my name is laven'

In[24]: print(s.capitalize())
My name is laven

In[25]: print(s.title())
My Name Is Laven

In[26]: print(s.lower())
my name is laven

In[27]: print(s.upper())
MY NAME IS LAVEN

In[28]: print(s.upper().lower())
my name is laven

In[29]: print('Hello World'.casefold()) # 不同的平台有不同的表现形式,但在同一平台下,表现形式相同,通常用来忽略大小写时的比较。
hello world

In[30]: print('Hello World'.swapcase())
hELLO wORLD

In[31]: print('\t'.expandtabs(4))
     # 此处前面有四个空格

大小写转化通常用在做比较的时候,当我们需要忽略大小写的时候,通常统一转化为全部大写或全部小写再做比较。

字符串修改之填充:

s = 'my name is laven'
help(s.center) # 默认空格填充。如果宽度小于等于原串长度,不做任何操作。
s.center(80)
>>> s.center(80)
'                                my name is laven                                '

>>> s.center(80, '#')
'################################my name is laven################################'
>>>

# ljust方法
>>> s.ljust(80)
'my name is laven                                                                '
>>>
>>> s.ljust(80, '*') # 字符串左对齐
'my name is laven****************************************************************'
>>>

# rjust方法
>>> s.rjust(80)
'

>>> s.rjust(80, '*')
'****************************************************************my name is laven'
# ljust,rjust方法是针对字符串的显示位置的

# zfill方法
>>> s.zfill(80)
'0000000000000000000000000000000000000000000000000000000000000000my name is laven'
>>>

接下来演示3个非常重的字符串方法:strip、lstrip、rstrip:

s = '   hahe hehe   \n \t'
s.strip()
>>> s.strip() # strip只能去掉字符串两边的空白或指定字符
'haha hehe'

# lstrip默认去掉前置的空白
>>> s.lstrip()
'haha hehe  \n \t'

# rstrip默认去掉后置的空白
>>> s.rstrip()
'    haha hehe'

# 去掉指定字符
>>> s = '##test##'
>>> s.strip('#')
'test'
>>> s.strip('*')
'##test##'

>>> s = '## test ##'
>>> s
'## test ##'
>>> s.strip('#')
' test '
>>> s.strip('#').strip()
'test'

strip方法可以移除指定的字符。大家可以试试看。

startswith与endswith方法,判断字符串是否以某个前缀开始,返回结果是boolean。

s = '**test##'
>>> s.startswith('*')
True
>>> s.endswith('#')
True
>>> s.endswith('test')
False
>>> s.endswith('test', 0, 5) # start,end 参数表示的是从索引start处开始到end处结束,但不包含end
False
>>> s.endswith('test', 0, 6)
True

字符查找与替换:

  • count
  • find
  • rfind
  • index
  • replace
# count方法
s = '**test##'
>>> s.count('*')
2
>>> s.count('#')
2
>>> s.find('t') # find从左向右查找
2
>>>
>>> s.find('t')
2
>>> s.find('test')
2
>>> s.rfind('test') # rfind是find的从右向左查找的版本
2
>>> s.rfind('t')
5

s = 'i very very love python'
s.find('very') # 2
s.find('very', 3) # start参数指定从哪里开始查找
s.find('very', 3, 10) # end参数指定到哪里结束查找,end不包含
s.rfind('very') # 从右往左查找,但得到的索引是从左到右

# index方法,index一个不存在的字符或子串时,会报错;而find则不会报错;
>>> s.index('t')
2
>>> s.index('test')
2
>>> s.rfind('t')
5
>>> s.find('a')
-1
>>> s.index('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found
s.find('a') # 使用find找一个不存在的字符,返回-1
s.index('a') # 使用index找一个不存在的字符,会抛出异常

# replace方法,可选的count,指定替换多少次
s = 'abc123abc123'
s.replace('abc', 'xyz') # 默认是全部替换,-1省略了
s.replace('abc', 'xyz', 1) # 只替换一次
s.replace('xxxx', '') # 如果要替换的字符不存在,什么都不做
s.replace('abc', 'xyz', -1) # -1表示全部替换

如果对find不是很理解,可以借助enumerate方法来查看,

In[32]: s = 'i very very love python'

In[33]: print(list(enumerate(s)))
[(0, 'i'), (1, ' '), (2, 'v'), (3, 'e'), (4, 'r'), (5, 'y'), (6, ' '), (7, 'v'), (8, 'e'), (9, 'r'), (10, 'y'), (11, ' '), (12, 'l'), (13, 'o'), (14, 'v'), (15, 'e'), (16, ' '), (17, 'p'), (18, 'y'), (19, 't'), (20, 'h'), (21, 'o'), (22, 'n')]

字符串判断函数是一些is开头的方法,这些方法用的不多。

一个比较有用的小技巧,

In[34]: line = 'url://http://lavenliu.cn'

In[35]: line.split(':', 1)
Out[35]: ['url', '//http://lavenliu.cn']

In[36]: key, value = line.split(':', 1)

In[37]: key
Out[37]: 'url'

In[38]: value
Out[38]: '//http://lavenliu.cn

splitlines方法:

In[39]: text = '''I am laven
    ...: I am a man
    ...: I like Emacs'''

In[40]: text.splitlines()
Out[40]: ['I am laven', 'I am a man', 'I like Emacs']

In[41]: text.splitlines(True) # 保留换行符
Out[41]: ['I am laven\n', 'I am a man\n', 'I like Emacs']

partition方法,

In[42]: s = 'my name is laven'

In[43]: s.partition(' ') # 类似于s.split(' ', 1)
Out[43]: ('my', ' ', 'name is laven')

In[44]: # 上面的line = 'url:http://magedu.com'可以写成如下的形式

In[45]: line.partition(':')
Out[45]: ('url', ':', '//http://lavenliu.cn')

In[46]: key, _, value = line.partition(':')

In[47]: key
Out[47]: 'url'

In[48]: value
Out[48]: '//http://lavenliu.cn'

In[49]: s.rpartition(' ')
Out[49]: ('my name is', ' ', 'laven')

字符串解包操作,

In[50]: s = 'my name is lavenliu'

In[51]: a, b, *mid, tail = s

In[52]: a
Out[52]: 'm'

In[53]: b
Out[53]: 'y'

In[54]: mid
Out[54]: 
[' ',
 'n',
 'a',
 'm',
 'e',
 ' ',
 'i',
 's',
 ' ',
 'l',
 'a',
 'v',
 'e',
 'n',
 'l',
 'i']

In[55]: tail
Out[55]: 'u'

字符串的迭代

字符串是也是可迭代的对象:

In[56]: s = 'hello world'

In[57]: for i in s:
    ...:     print(i)
    ...:     
h
e
l
l
o
 
w
o
r
l
d

In[58]:

切片及索引

In[1]: s = "use python do something"

In[2]: s[1], s[-1], s[1:6:2], s[1:], s[:-1], s[:]
Out[2]: 
('s',
 'g',
 's y',
 'se python do something',
 'use python do somethin',
 'use python do something')

In[3]: s[1:] # 从1开始到最后
Out[3]: 'se python do something'

In[4]: # 倒序
In[5]: s[::-1]
Out[5]: 'gnihtemos od nohtyp esu'

In[7]: s[4]
Out[7]: 'p'

字符串格式化

字符串格式化是拼接字符的一种手段。如:

In[8]: print(' '.join(['i', 'love', 'python']))
i love python

In[9]: print('i' + ' love ' + 'python')
i love python

join+拼接字符串难以控制格式。接下来介绍两种字符串格式化的方法。一种是printf-style方式一种是format方式。

printf风格的格式化

首先介绍一下print函数的占位符及其说明,后面的讲解会用得到。

占位符

说明

i

有符号整数数

d

有符号整数

o

有符号的八进制数

x

十六进制(以小写显示)

X

十六进制(以大写显示)

e

科学计数法(以小写显示)

E

科学计数法(以大写显示)

f

浮点数

F

浮点数

语法为:

template % tuple
>>> 'I am %s' % ('lavenliu',) # 如果只有一个元素的时候,可以不用元组;即这里可以省略逗号,或直接省略小括号。
'I am lavenliu'

template % dict
>>> 'I am %(name)s' % {'name': 'lavenliu'}
'I am lavenliu'
>>>
>>> 'I am %(name)s, my name is %(name)s' % {'name': 'lavenliu'}
'I am lavenliu, my name is lavenliu'
# 使用字典的形式的场景
## 1. 反复出现
## 2. 需要格式化内容很多

一个简单的例子,

>>> a = "this is %s %s" % ("my", 'apple')
>>> print(a)
this is my apple

占位符的演示,

>>> '%i' % 18
'18'
>>> '%d' % 18
'18'
>>> '%ld' % 18 # 为了与C语言的兼容
'18'
>>> '%o' % 18
'22'
>>> '%X' % 12
'C'
>>> '%x' % 12
'c'
>>> '%e' % 0.00000345
'3.450000e-06'
>>> '%E' % 0.00000345
'3.450000E-06'
>>> '%e' % 12
'1.200000e+01'
>>> '%f' % 0.00000345 # 默认显示6位,不足就补0,多了就舍去
'0.000003'
# 如果只显示3位小数呢
'%0.3f' % 0.00123
'0.001'
>>> '%F' % 0.00000345
'0.000003'
# r与s的区别
class A:
    def __str__(self):
        return 'I am A.__str__'


    def __repr__(self):
        return 'I am A.__repr__'


a = A()
>>> a = A()
>>> '%s' % 123
'123'
>>> '%s' % a
'I am A.__str__'
>>> '%r' % a
'I am A.__repr__'
>>> '%a' % '\n'
"'\\n'"
# str是给人看的,repr是个机器看的

# a的演示
In[11]: '%a' % '\n'
Out[11]: "'\\n'"

In[10]: '%a' % '大川淘气'
Out[10]: "'\\u5927\\u5ddd\\u6dd8\\u6c14'"

当类型不匹配时,会抛出TypeError。当占位符是%s时,其实隐式调用了str()。

format风格的字符串格式化

format语法,使用大括号作为占位符。当调用format方法时,format传入的参数会替换大括号。format方法的参数个数是可变的。

'I am {}'.format('lavenliu')
'I am {}, my age is {}'.format('lavenliu', 23) # 按照顺序
# 如果按照顺序呢
# 可以在占位符里加数字指定format参数的位置
'I am {1}, my age is {0}'.format('18, 'lavenliu'')

# 可以在占位符里加标识符,以使用关键字参数
'I am {name}, my age is {age}'.format(name='lavenliu', age=18)
# 多次出现也是可以的
'I am {name}, my name is {name}'.format('lavenliu')
'I am {0}, my name is {0}'.format('lavenliu')

# 几个好玩的用法
## 要么全是位置与关键字的
## 要么全是顺序与关键字的
## 位置的要在关键字之前
>>> '{1} {0} {name}'.format(1, 2, name='abc')
'2 1 abc'
>>> '{} {} {name}'.format(1, 2, name='abc')
'1 2 abc'

>>> '{0} {name} {1}'.format(1, 2, name='abc')
'1 abc 2'

>>> '{name} {} {}'.format(1, 2, name='abc')
'abc 1 2'

>>> '{} {name} {}'.format(1, 2, name='abc')
'1 abc 2'

# 这样写就会出错
## 顺序的,位置的,关键字的,不要混用
>>> '{} {1} {name}'.format(1, 2, name='abc')
ValueError: cannot switch from automatic field numbering to manual field specification

# 位置参数要在关键字参数之前
>>> '{} {name} {}'.format(1, name='abc', 2)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

几个format的小例子,

In[12]: b = "this is {} {}" .format("my", "apple")

In[13]: print(b)
this is my apple

In[14]: ## {}里可以加入参数的位置

In[15]: b = "this is {1} {0}" .format("apple", "my")

In[16]: print(b)
this is my apple

In[17]: ## 更加高级的用法,这里不用指定数字,因为有可能算错位置

In[18]: b = "this is {whose} {fruit}" .format(fruit="apple", whose="my")

In[19]: print(b)
this is my apple

还可以使用类进行格式化,很吊的,

class A:
    def __init__(self):
        self.x = 1
        self.y = 2


>>> a = A()
>>> a.x
1
>>> a.y
2

>>> '{0.x} {0.y}'.format(a)
'1 2'

>>> '{instance.x}'.format(instance=a)
'1'

可以使用列表进行格式化,

>>> lst = [1, 2, 3]
>>> '{0[0]}'.format(lst)
'1'
>>> '{lst[0]}'.format(lst=lst)
'1'

再来看几个例子,主要涉及格式化时的对齐操作,

# < 左对齐
>>> '{0:<80}'.format('lavenliu')
'lavenliu                                                                          '

# > 右对齐
>>> '{0:>80}'.format('lavenliu')

# ^ 居中对齐
'{0:^80}'.format('lavenliu')

# 默认的,字符串对齐方式是左对齐
'{0:80}'.format('lavenliu')

# 默认的,数字对齐方式是右对齐
'{0:80}'.format(10)

# 数字
'{0:d}'.format(10)
'{:n}'.format(1000)
'{:b}'.format(10)

# 还可以嵌套
>>> '{0:^{width}}'.format('lavenliu', width=80)
'                                     lavenliu                                     '

>>> '{0:#^{width}}'.format('lavenliu', width=80)
'#####################################lavenliu#####################################'

>>> '{0:{fill}^{width}}'.format('lavenliu', width=80, fill='*')
'*************************************lavenliu*************************************'

printf-style格式化对于从其他语言,尤其是C语言转过来的,非常容易接受,但是Python并不推荐这种方法。 尽量使用内置的这种format的方式来格式化字符串。

另外,也可以使用字典的方式实现字符串的格式化。

In[20]: a = "this is %(whose)s %(fruit)s" % {'whose': 'my', 'fruit': 'apple'}

In[21]: a
Out[21]: 'this is my apple'

今日总结

  1. 字符串是不可变的数据类型;
  2. 字符串可以进行索引、切片、迭代等操作;
  3. 字符串内置了很多方法供我们使用;
  4. Python3中的字符默认是Unicode格式的;

格式化总结

  1. 占位符与参数不匹配,会抛出异常
  2. {} 按照顺序,使用位置参数
  3. {数字 i} 会把位置参数当成一个列表 args,args[i] 当i不是args的索引的时候,抛出IndexError
  4. {关键字 k} 会把关键字参数当成一个字典kwargs,使用kwargs[k] 当k不是kwargs的key时,会抛出KeyError
  5. 如果要单纯的打印大括号,可以使用{{}};如果要打印形如{18}的形式,可以使用{{{}}}

原文发布于微信公众号 - 小白的技术客栈(XBDJSKZ)

原文发表时间:2017-08-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

[基础篇]Go语言变量

变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。变量可以通过变量名访问。 Go 语言变量名由字母、数字、下划线组成,其中首个字母不能为数字。 声...

3517
来自专栏GopherCoder

Python 强化训练:第三篇

963
来自专栏小樱的经验随笔

【Java学习笔记之十四】Java中this用法小节

用类名定义一个变量的时候,定义的只是一个引用,外面可以通过这个引用来访问这个类里面的属性和方法。     那们类里面是够也应该有一个引用来访问自己的属性和方法纳...

2676
来自专栏鸿的学习笔记

有趣的Scala模式匹配

它被称为模式匹配,模式匹配包含了一系列以case关键字开头的分支,每一个分支包含一个模式或者是多个表达式。模式有很多种,例如常量模式('*',1),变量模式(可...

664
来自专栏开发与安全

从零开始学C++之构造函数与析构函数(三):深拷贝与浅拷贝、空类与空数组

一、深拷贝与浅拷贝 说得简单点,假设一个类有指针成员,如果在拷贝的时候顺带连指针指向的内存也分配了,就称为深拷贝,如下图(v2 从 v 拷贝而来): ? 如果只...

1K0
来自专栏我的博客

Go的基础知识1

1.关键字 break    default      func    interface    select case     defer        ...

3459
来自专栏数据结构与算法

01:数制转换

01:数制转换 总时间限制: 1000ms 内存限制: 65536kB描述 求任意两个不同进制非负整数的转换(2进制~16进制),所给整数在long所能表达...

3757
来自专栏和蔼的张星的图像处理专栏

212. 空格替换先扩充,从后往前处理

设计一种方法,将一个字符串中的所有空格替换成 %20 。你可以假设该字符串有足够的空间来加入新的字符,且你得到的是“真实的”字符长度。 你的程序还需要返回被替...

492
来自专栏进击的君君的前端之路

正则表达式

1485
来自专栏编程

Python面向对象1:基础介绍+封装特征

目前有三种编程方式: 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封...

1827

扫码关注云+社区