Python学习日志
RBHGO的主页欢迎关注
温馨提示:创作不易,如有转载,注明出处,感谢配合~
目录
文章目录
Python学习日志06课 - 字符串
因为字符串和列表、元组比较相似,所以在分享另外两容器型数据类型之前;这一课先和大家系统的分享一下字符串的知识。在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。字符串中的字符可以是特殊符号
、英文字母
、中文字符
、日文的平假名或片假名
、希腊字母
、Emoji字符
等。其实我在学习日志03中也分享过字符串的知识,现在再和大家更详细的分享一下关于字符串的知识。
前言
稍微和大家分享一下关于渐进时间复杂度和渐进空间复杂度。我们评价一个算法的效率主要是看它的就是它的时间复杂度和空间复杂度情况。可能有的开发者接触时间复杂度和空间复杂度的优化不太多(尤其是客户端),但在服务端的应用是比较广泛的,在巨大并发量的情况下,小部分时间复杂度或空间复杂度上的优化都能带来巨大的性能提升,是非常有必要了解的。
算法:解决问题的方法(步骤)。
评价算法好坏的标准︰渐近时间复杂度和渐近空间复杂度。
渐近时间复杂度 ---> 大0标记(Big o Natation)--->T(n) = O(f(n))
n是影响复杂度变化的因子,f(n)是复杂度具体的算法。
下面这些复杂度是属于不能容忍的类型,我们一定要避免这些情况的出现。
O(N**N)
o(N!)
0(2**N)
------------------------------------------------------------------
下面的复杂度是可以接受的、是比较好的
O(c) ---> 常量时间复杂度
简单排序算法:O(N**2) ---> 平方时间复杂度
找最大找最小: O(N) ---> 线性时间复杂度
折半查找(二分查找):O(log_2 N) ---> 对数时间复杂度
高级排序算法:O(N * log2_N) ---> 线性对数时间复杂度
然后每天都不能忘了做题,题目是“有一个放整数的列表,找出列表中出现次数最多的元素。”
重点在于找出出现最多次元素的同时计数。
# 有一个放整数的列表,找出列表中出现次数最多的元素。
nums = [10, 10, 1, 1, 10, 100, 100, 1, 10, 1, 1, 10]
# 命名了两个空列表,将列表中的第一个数以及第一个数出现的次数分别放进两个列表
items, max_counter = [nums[0]], nums.count(nums[0])
for num in nums[1:]:
# 从第二个元素开始的遍历,把数放进一个用来计数的命名中
curr_counter = nums.count(num)
# 进行判断,如果有一个元素,比原命名列表中的数除了的次数大,执行下面语句
if curr_counter > max_counter:
items.clear()
items.append(num)
max_counter = curr_counter
# 还要设置一个判断,因为列表中可能会出现多个数出现一样的次数且都是最多
elif curr_counter == max_counter:
if num not in items:
items.append(num)
for item in items:
print(f'出现最多次的数是{item},他出现了{max_counter}次')
进入正题
字符串的运算
(1).获取字符串长度
获取字符串长度没有直接的运算符,而是使用内置函数len
,我们在上节课的提到过这个内置函数,代码如下所示。
a = 'nishi, zhendenb'
# 获取字符串的长度
print(len(a))
(2).比较和成员运算
对于两个字符串类型的变量,可以直接使用比较运算符比较两个字符串的相等性或大小。需要说明的是,因为字符串在计算机内存中也是以二进制形式存在的,那么字符串的大小比较比的是每个字符对应的编码的大小。例如A
的编码是65
, 而a
的编码是97
,所以'A' < 'a'
的结果相当于就是65 < 97
的结果,很显然是True
;
a = 'nishi, zhendenb'
# 成员运算
print('hi' in a) # 运行结果:True
print('or' in a) # 运行结果:False
# 比较运算
b = 'hello, world'
print(a == b) # 运行结果:False
print(a != b) # 运行结果:True
c = 'goodbye, world'
print(b >= c) # 运行结果:True
需要强调一下的是,字符串的比较运算比较的是字符串的内容,Python中还有一个is
运算符**(身份运算符)**,如果用is
来比较两个字符串,它比较的是两个变量对应的字符串是否在内存中相同的位置(内存地址),简单的说就是两个变量是否对应内存中的同一个字符串。看看下面的代码就比较清楚is
运算符的作用了。
s1 = 'hello world'
s2 = 'hello world'
# 比较字符串的内存地址
print(s1 is s2, s2 is s3) # 运行结果:False True
(3).拼接和重复
下面的例子演示了使用+
和*
运算符来实现字符串的拼接和重复操作。
# 拼接
p = 'hello, world'
e ='!!!'
print(p + e) # 运行结果:hello, world!!!
# 重复运算
print(p * 2) # 运行结果:hello, worldhello, world
(4).索引和切片
如果希望从字符串中取出某个字符,我们可以对字符串进行索引运算,运算符是[n]
,其中n
是一个整数,假设字符串的长度为N
,那么n
可以是从0
到N-1
的整数,其中0
是字符串中第一个字符的索引,而N-1
是字符串中最后一个字符的索引,通常称之为正向索引;在Python中,字符串的索引也可以是从-1
到-N
的整数,其中-1
是最后一个字符的索引,而-N
则是第一个字符的索引,通常称之为负向索引;记住第一个字符的索引是0
或-N
,最后一个字符的索引是N-1
或-1
就行了。
# 字符串的索引和切片
# 提示:如果正向和逆向取字符串里同一元素,那么数字绝对值之和应该等于字符串长度
# 索引
p = 'hello, world'
print(p[len(p) - 1], p[-1]) # 运行结果:d d
print(p[0], p[-len(p)]) # 运行结果:h h
print(p[5], p[-7]) # 运行结果:, ,
s = 'abc123456'
# 获取第一个字符
print(s[0], s[-N]) # 运行结果:a a
# 获取最后一个字符
print(s[N-1], s[-1]) # 运行结果:6 6
# 切片
# 输出2-4切片
print(p[2:5]) # 运行结果:llo
# 输出1-9间隔切片
print(p[1:10:2]) # 运行结果:el,wr
# 输出翻转的字符串
print(p[::-1]) # 运行结果:dlrow ,olleh
温馨提示:因为字符串是不可变类型,所以不能通过索引运算修改字符串中的字符。
还有一点需要提醒大家注意的是,在进行索引操作时,如果索引越界(正向索引不在
0
到N-1
范围,负向索引不在-1
到-N
范围),会引发IndexError
异常,错误提示信息为:string index out of range
(字符串索引超出范围)。
(5).循环遍历
如果希望从字符串中取出每个字符,可以使用for
循环对字符串进行遍历,有两种方式。
方式一:
a1 = 'hello'
for i in range(len(a1)):
print(a1[i])
方式二:
a2 = 'hello'
for a in a2:
print(a)
字符串的方法
(1).转义字符和原始字符串
a ='"hello, world"'
print(a) # 运行结果 "hello, world"
b ="\"goodbye, world\""
print(b) # 运行结果 "goodbye, world"
a1 = 'hello, \tworld'
print(a1) # 运行结果 hello, world
a2 = 'hello, \nworld'
print(a2) # 运行结果 hello,
# world
a3 = 'hello, world\b'
print(a3) # 运行结果 hello, worl
c = '''
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
'''
print(c)
# 字符串s1中\t是制表符,\n是换行符
s1 = '\time up \now'
print(s1)
# 字符串s2中没有转义字符,每个字符都是原始含义
s2 = r'\time up \now'
print(s2)
温馨提示:
- 可以在单引号里打双引号,双引号里也可以打单引
- 不能直接在单引号里打单引号,双引号里也不能直接打双引号
- 如果想要这样做必须要转义字符
(\)
才能实现;其他转义字符(\n
:换行)(\t
:制表键)(\b
:从后面去掉一个)所以当我们需要读文件时(或者说输出\
时),需要输出一个\
,往往得打两个\
才能输出一个\
- 当然也可以使用
R'字符串'
或者r'字符串'
:原始字符串,每个字符都是它原始的含义,没有转义字符f'字符串'
:带占位符的字符串(格式化字符串)
Python中还允许在\
后面还可以跟一个八进制或者十六进制数来表示字符,例如\141
和\x61
都代表小写字母a
,前者是八进制的表示法,后者是十六进制的表示法。另外一种表示字符的方式是在\u
后面跟Unicode字符编码,例如\u738b\u5927\u9524
代表的是中文“王大锤”。运行下面的代码,看看输出了什么。
s1 = '\141\142\143\x61\x62\x63' # 运行结果:abcabc
# ASCII ---> GB2312 ---> GBK ---> unicode(统一码/万国码)(UTF-8)
s2 = '\u738b\u5927\u9524' # 运行结果:王大锤
print(s1, s2)
小技巧:要想运行自己的名字
print(hex(ord('姓')), hex(ord('名')))
然后再用
\u
刚刚得到的编码就可以输出来。
(2).大小写相关操作
下面的代码演示了和字符串大小写变换相关的方法。
a = 'i want to see YOU'
# 全转大写
print(a.upper())
# 全转小写
print(a.lower())
# 首单词首字母大写
print(a.capitalize())
# 每个单词首字母大写
print(a.title())
(3).性质判断
可以通过字符串的startswith
、endswith
来判断字符串是否以某个字符串开头和结尾;还可以用is
开头的方法判断字符串的特征,这些方法都返回布尔值,代码如下所示。
# 判断字符串相关属性的方法
b = 'abc369'
# 判断字符串中是不是全是数字返回布尔值
print(b.isdigit())
# 判断字符串中是不是全是字母返回布尔值
print(b.isalpha())
# 判断字符串中是不是由数字和字母构成返回布尔值
print(b.isalnum())
# 判断字符串是不是ASCII码字符返回布尔值
print(b.isascii())
c = '你真细'
# 判断字符串是否以指定内容开头返回布尔值
print(c.startswith('你'))
# 判断字符串是否以指定内容结尾返回布尔值
print(c.endswith('真细'))
(4).查找操作
如果想在一个字符串中从前向后查找有没有另外一个字符串,可以使用字符串的find
或index
方法。
# 在字符串中查找有没有某个子串的操作
a = 'Oh apple, i love apple.'
# index -从左向右寻找指定的子串(substring),可以指定从
# 找到了返回子串对应的索引(下标),找不到直接报错(程序崩溃)
print(a.index(' apple'))
print(a.index(' apple', 10))
print(a.rindex(' apple'))
# ValueError: substring not found
# print(a.index( ' banana' ))
# 从前向后查找字符o出现的位置(相当于第一次出现)
print(a.find(' apple')) # 运行结果:3
# 从索引为10的位置开始查找字符apple出现的位置
print(a.find(' apple', 10)) # 运行结果:16
# 从后向前查找字符apple出现的位置(相当于最后一次出现)
print(a.rfind(' apple')) # 运行结果:16
# 找不到都会返回-1
print(a.find('banana')) # 运行结果:-1
print(a .rfind('banana ')) # 运行结果:-1
温馨提示:在使用
find
和index
方法时还可以通过方法的参数来指定查找的范围,也就是查找不必从索引为0
的位置开始。find
和index
方法还有逆向查找(从后向前查找)的版本,分别是rfind
和rindex
。
(5).格式化字符串
在Python中,字符串类型可以通过center
、ljust
、rjust
方法做居中、左对齐和右对齐的处理。
s = 'hello, world'
# center方法以宽度20将字符串居中并在两侧填充*
print(s.center(20, '*')) # ****hello, world****
# rjust方法以宽度20将字符串右对齐并在左侧填充空格
print(s.rjust(20)) # hello, world
# ljust方法以宽度20将字符串左对齐并在右侧填充~
print(s.ljust(20, '~')) # hello, world~~~~~~~~
在用print
函数输出字符串时,可以用下面的方式对字符串进行格式化。
a = 321
b = 123
print('%d * %d = %d' % (a, b, a * b))
当然,我们也可以用字符串的方法来完成字符串的格式,代码如下所示。
a = 321
b = 123
print('{0} * {1} = {2}'.format(a, b, a * b))
从Python 3.6开始,格式化字符串还有更为简洁的书写方式,就是在字符串前加上f
来格式化字符串,在这种以f
打头的字符串中,{变量名}
是一个占位符,会被变量对应的值将其替换掉,代码如下所示。(我编写的很多代码都使用了这种方式,相信大家也留心了)
a = 321
b = 123
print(f'{a} * {b} = {a * b}')
如果需要进一步控制格式化语法中变量值的形式,可以参照下面的表格来进行字符串格式化操作。
变量值 | 占位符 | 格式化结果 | 说明 |
---|---|---|---|
3.1415926 | {:.2f} | '3.14' | 保留小数点后两位 |
3.1415926 | {:+.2f} | '+3.14' | 带符号保留小数点后两位 |
-1 | {:+.2f} | '-1.00' | 带符号保留小数点后两位 |
3.1415926 | {:.0f} | '3' | 不带小数 |
123 | {:0>10d} | 0000000123 | 左边补0 ,补够10位 |
123 | {:x<10d} | 123xxxxxxx | 右边补x ,补够10位 |
123 | {:>10d} | ' 123' | 左边补空格,补够10位 |
123 | {:<10d} | '123 ' | 右边补空格,补够10位 |
123456789 | {:,} | '123,456,789' | 逗号分隔格式 |
0.123 | {:.2%} | '12.30%' | 百分比格式 |
123456789 | {:.2e} | '1.23e+08' | 科学计数法格式 |
a = 123456789
print(f'{a:.2e}') # 运行结果:1.23e+08
b = 0.123
print(f'{b:.2%}') # 运行结果:12.30%
c = 123456789
print(f'{c:,}') # 运行结果:123,456,789
(6).修剪操作
字符串的strip
方法可以帮我们获得将原字符串修剪掉左右两端空格之后的字符串。这个方法非常有实用价值,通常用来将用户输入中因为不小心键入的头尾空格去掉,strip
方法还有lstrip
和rstrip
两个版本。
s = ' 乌兹!YYDS!!! \t\r\n'
# strip方法获得字符串修剪左右两侧空格之后的字符串
print(s.strip()) # 运行结果:乌兹!YYDS!!!
# lstrip方法获得字符串修剪左侧空格之后的字符串
print(s.lstrip()) # 运行结果:乌兹!YYDS!!!
# rstrip方法获得字符串修剪右侧空格之后的字符串
print(s.rstrip()) # 运行结果: 乌兹!YYDS!!!
(7).拆分、合并
sentence = 'It is boring. You keep saying I am bored'
sentence2 = sentence.replace(',', ' ').replace('.', ' ')
# 用空格拆分字符串得到一个列表
words = sentence2.split()
print(words, len(words))
for word in words:
print(word)
# 用空格拆分字符串,最多允许拆分3次
words = sentence2.split(' ', maxsplit=3)
print(words, len(words))
# 从右向左进行字符串拆分,最多运行拆分3次(没有lsplit用来拆分,因为split就是从左到右拆分)
words = sentence2.rsplit(' ', maxsplit=3)
print(words, len(words))
# 用逗号拆分字符串
items = sentence. split(', ')
for item in items:
print(item)
w = '''
浩瀚星空, 满目苍穹, 冰冷的光, 荧荧发亮, 闭上双眼, 松开想象, 我的那颗, 烁烁闪动;
漫漫长夜, 思念幻想, 准备好么? 你的行囊! 明天就要, 独自去往, 迎着正午, 向着朝阳;
'''
# 将列表中的元素用指定的字符串连接起来
print('*'.join(w))
(8).编码和解码
a = '我爱你中国啊'
# GBK <--- GB2312 <---- ASCII
b = a.encode('gbk')
print(type(b))
print(b, len(b))
c = b'\xce\xd2\xb0\xae\xc4\xe3\xd6\xd0\xb9\xfa\xb0\xa1'
print(c.decode('gbk'))
a = '我爱你中国, 亲爱的母亲'
# UTF-8编码是Unicode (万国码)的一种实现方案
b = a.encode('utf-8')
print(type(b))
print(b, len(b))
c = b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0\xe4\xb8\xad\xe5\x9b\xbd'
# 如果编码和解码的方式不一致,Python中可能会产生UnicodeDecodeError异常
# 也有可能会出现乱码现象
print(c.decode(' gbk'))
print(c.decode(' utf-8'))
a = '我爱你中国亲爱的母亲'
# latin-1编码不能够用来处理中文字符(因为会产生编码黑洞,中文全部变成?,想解码都没有机会。
b = a.encode('iso-8859-1')
# UTF-8是—种变长编码
# 表示数字和英文字母的时候,只需要1个字节
# 表示中文的时候,需要3个字节
# 表示Emoji字符的时候,需要4个字节
# 有些字符是占用2个字节
a = '👴✌是🎅🏿☝🏻'
b = a.encode()
print(b, len(b))
print(b.decode())
温馨提示:
str(字符串) —> encode(字符集) ----> bytes(字节串)
要点:
- 选择字符集(编码)的时候,最佳的选择(也是默认的)是UTF-8编码。
- 编码和解码的字符集要保持一致,否则就会出现乱码现象。
- 不能用ISO-8859-1编码保存中文,否则会出现编码黑洞,中文变成?。
- UTF-8是一种变长的编码,最少1个字节(字母和数字),最多4个字节(Emoji),表示中文用3个字节。
(9).其他
# translate方法实现凯撒密码 – 通过对应字符的替换,实现对明文进行加密的一种方式。
message = 'attack at dawn'
table = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'defghijklmnopqrstuvwxyzabc')
print(message.translate(table))
温馨提示:
对称加密:加密和解密使用了相同的密钥。—> AES
非对称加密:加密和解密使用了不同的密钥(公钥、私钥)。 —> RSA —> 比较适合互联网应用
总结
知道如何表示和操作字符串对程序员来说是非常重要的,因为我们需要处理文本信息,Python中操作字符串可以用拼接、切片等运算符,也可以使用字符串类型的方法。
感谢学习陪伴,您的点赞,评论就是我更新的动力