表示一个字符串就是用引号将一连串字符包裹起来,或者使用str()构造器。
打开Pycharm集成开发环境,输入下面的代码:
a = 'This is a string' # 用单引号
b = "Selenium automates browsers. That's it!" # 如果字符串内容包含单引号,那么最好用双引号将其括起来
c = """ # 用三个引号,定义多行字符串
This is a multiple lines string
This is the second line
"""
d = str('hah') # str构造器
e = '*'*10 # 连续10个星号
print(a)
print(b)
print(c)
print(d)
print(e)
在Pycharm的编辑器窗口上,点击右键,点击Run,可以看到下面的输出:
This is a string
This is another string
This is a multiple lines string
This is the second line
hah
**********
下面这几个字符串来自string.py源码:
whitespace = ' \t\n\r\v\f'
ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ascii_letters = ascii_lowercase + ascii_uppercase
digits = '0123456789'
hexdigits = digits + 'abcdef' + 'ABCDEF'
octdigits = '01234567'
punctuation = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
printable = digits + ascii_letters + punctuation + whitespace
想使用这些字符串,可以通过下面的方式:
import string
print(string.printable)
查询一下Python内置的字符串操作方法都有哪些。在Python交互式界面上,输入下面的代码:
>>> dir('')
按回车键,你会看到下面的这样的输出信息:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
这些都是Python为字符串类型的数据提供的内置方法。这些方法的命名让我们非常直观的猜测的其用途,我们自己命名变量和函数时,也最好起有意义的名字。
从上面的输出中看到lower这个函数,猜测可能用来将一个字符串中的大写字母转成小写字母用的。为了验证这一点,可以输入下面的命令具体查看一下:
>>> help(''.lower)
按回车键,将看到这个函数的用法信息:
Help on built-in function lower:
lower() method of builtins.str instance
Return a copy of the string converted to lowercase.
从描述中看到,lower函数可以将一个字符串转换成小写字母。实际编写一段代码验证一下:
从描述中看到,lower函数可以将一个字符串转换成小写字母。实际编写一段代码验证一下:
>>> 'HADFADFA'.lower()
'hadfadfa'
哈哈,符合我们的猜测。接下来,介绍几种在工作中对字符串的几种常见操作。
通过索引和切片的方式读取字符串中的字符或者子串。
name = 'jason'
print(name[0]) # 输出j
print(name[1:3]) # 输出as,下标从1到3,但不包含3
利用切片操作,或者reversed函数,将字符串反转:
name = 'Python'
print(name[::-1])
print(''.join(reversed(name))) # reversed返回的是一个reversed object,要join一下
name = 'jmeter'
name = name.replace('j', 'J')
print(name) # Jmeter
两种拼接方法,一种是加号,一种是join方法。
加号拼接,就是直接将两个字符串连接在一起:
name = 'automation'
gender = 'testing'
print(name+gender) # 输出automationtesting
另一种join方法,可以将多个独立的字符或者字符串,或者一个列表里面的数据,用特定的字符拼接起来。
print(' '.join([name, gender])) # 输出automation testing ,中间一个空格
print('&'.join([name, gender])) # 输出automation&testing ,中间一个&
通过内置的split方法,可以将做一个字符串切分成一段一段的,放到一个列表中。实际工作中,再通过索引操作取得想要的数据。比如将Python单元测试框架Pytest的slogan用空格分割成列表:
string="pytest: helps you write better programs" # 每个单词之间用一个空格隔开
print(string.split(' ')) # 输出 ['pytest:', 'helps', 'you', 'write', 'better', 'programs']
再来看一个例子,从web自动化测试工具selenium下载网址中提取域名:
path = 'https://www.selenium.dev/downloads/'
namespace = path.split('//')[1].split('/')[0]
print(namespace)) # 返回'www.selenium.dev'
如果单词之间用多个空格,或者其他空白符隔开时,那么不给split方法传参就可以了:
pytest="pytest: helps you write \t better \n programs" # helps前面有多个空格
print(pytest.split()) # ['pytest:', 'helps', 'you', 'write', 'better', 'programs']
这是因为split方法默认是使用ASCII whitespace characters对字符串进行分割的。具体描述可以参考split函数的官方文档(在Pycharm中通过command+点击split方式进入)。
在接收用户输入时,避免用户在输入数据两边意外加入空格或者其他空白符,可以对收到数据两边的空白(空格和制表符)进行去除,例如:
s = ' The Selenium Server is needed in order to run Remote Selenium WebDriver.\t'
print(s.strip()) # 输出'The Selenium Server is needed in order to run Remote Selenium WebDriver.'
print(s.rstrip()) # 输出 ' The Selenium Server is needed in order to run Remote Selenium WebDriver'
print(s.lstrip()) # 输出 'The Selenium Server is needed in order to run Remote Selenium WebDriver. '
还可以删除其他字符,将待删除的字符作为参数,比如:
s = '[Locust is an easy-to-use, distributed, user load testing tool.]'
print(s.rstrip(']')) # 输出 '[Locust is an easy-to-use, distributed, user load testing tool.'
print(s.lstrip('[')) # 输出 'Locust is an easy-to-use, distributed, user load testing tool.]'
判断 a 串是否为 b 串的子串。
selenium = "Selenium automates browsers. That's it!"
b = 'browsers'
r = True if b in selenium else False
在主串中查找子串,返回主串中匹配子串的最小索引。
a = 'co'
b = 'Locust is completely event-based, and therefore it’s possible to support thousands of concurrent users on a single machine.'
print(b.find(a)) # 默认从下标0开始查找,返回10
print(b.find(a, 20)) # 从下标20开始查找,返回86
'Locust supports running load tests distributed over multiple machines.'.count('o') # 4
"split the argument into words using split".capitalize() # 'Split the argument into words using split'
两种方式,一种是使用%,另外一种是使用format函数。
%s表示字符串,%d表示十进制数,%%表示百分号。
print("The %s application is open source software, a %d%% pure Java application designed to load test functional behavior and measure performance. " % ("Apache JMeter™", 100))
format格式化,需要在字符串模板中用{}占位,如果有多个{},format函数中的参数将按顺序传递给{}。例如:
print('{} is compatible with Java {} or higher.'.format('JMeter', 8))
Jmeter传递个第一个{},8传递给第二个{}。
字符串模板中{}里面还可以指定变量名,然后通过传入字典或者变量值给format函数,例如:
welcome_str = '{tool} is a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the {language} world.'
welcome_dic = {'tool': 'Pipenv', 'language': 'Python'}
print(welcome_str.format(**welcome_dic)) # 传入字典
print(welcome_str.format(tool='Pipenv', language='Python')) # 传入变量值
f字符串是一种更加容易的、简便的格式化方法,在Python 3.6开始加入标准库。在一个字符串开头有一个大写的F或者小写的f,大括号中的变量被值替换。注意:!,:{} ;不能出现在{}里面否则会报错。
startwith = True
otherwise = False
print(f"str.startswith Return {startwith} if string starts with the prefix, otherwise return {otherwise}.")
更加强大的是,大括号{}可以对表达式求值,或者执行函数。
print(f"{ 2 * 3 + 1}")
name='pytest'
print(f"{name.title()} is a mature full-featured Python testing tool that helps you write better programs.")
Python字符串是不可改变的,Python的不可改变性体现在:
下面这段代码,开始时通过id()查看变量str1的身份是4314674880,与str2拼接之后,str1指向了新的字符串,而不是在原来的str1上修改的。再次查看str1的身份,已经是4316047728了,与最开始身份已经不同了,说明不是原来的对象了。
str1="Selenium is not just one tool or API "
str2="but it composes many tools."
print(id(str1)) # 输出4314674880
str1+=str2
print(str1) # 输出Selenium is not just one tool or API but it composes many tools.
print(id(str1)) # 输出 4316047728,可见str1的身份已经变了
如果想在字符串中输入一些特殊字符,需要借助转义字符。比如:
print("Hello\nWorld") # Hello 和World会放在两行
print("Hello\"World\"") # 输出 Hello"World"
如果要取消转义字符的效果,可以在字符串前面添加一个r
print(r"Hello\"World\"") # 原样输出Hello\"World\"
对字符串进行加密,可以使用hashlib库。
import hashlib
print(hashlib.md5("Nobody inspects".encode('utf-8')).hexdigest()) # md5加密
print(hashlib.sha224("Nobody inspects the spammish repetition".encode('utf-8')).hexdigest()) # sha224加密
字符串封装的方法,处理一般的字符串操作,还能应付。但是,稍微复杂点的字符串处理任务,需要靠正则表达式,简洁且强大。
Python中的正则,是通过模块re来支持的,因此使用正则表达式时,要先import re
导入re模块。
首先,认识常用的元字符
.
匹配除 “\n” 和 “\r” 之外的任何单个字符。^
匹配字符串开始位置$
匹配字符串中结束的位置*
前面的原子重复 0 次、1 次、多次?
前面的原子重复 0 次或者 1 次+
前面的原子重复 1 次或多次{n}
前面的原子出现了 n 次{n,}
前面的原子至少出现 n 次{n,m}
前面的原子出现次数介于 n-m 之间( )
分组,输出需要的部分再认识常用的通用字符:
\s
匹配空白字符\w
匹配任意字母/数字/下划线\W
和小写 w 相反,匹配任意字母/数字/下划线以外的字符\d
匹配十进制数字\D
匹配除了十进制数以外的值[0-9]
匹配一个 0~9 之间的数字[a-z]
匹配小写英文字母[A-Z]
匹配大写英文字母正则表达式,常会涉及到以上这些元字符或通用字符。
import re
s = 'Life is short, I use Python'
pattern = 'Python'
r = re.search(pattern, s)
print(r.span()) # (21, 27)
其实,正则模块中还有一个match方法,不过它只会从主串的第一个字符开始匹配。第一个字符不匹配就返回None。
recom = re.compile('Python')
print(recom.match(s)) # 返回 None,找不到匹配,match只能匹配Python开头的字符串
import re
s = "当月最后一天:2019-12-31"
pattern = r"\d+"
r = re.findall(pattern, s)
print(r)
findall如果匹配的数据太多,可能导致内存问题。可以使用finditer方法,返回一个迭代器,通过迭代器里面的对象 re.Match的 span 找出匹配位置。
import re
s = "当月最后一天:2019-12-31"
pattern = r"\d+"
r = re.finditer(pattern, s)
for i in r:
print(i.span())
如果打算一个模式可以被多次使用,那么可以提前将模式pattern编译成对象,另外,优先编译成正则对象,然后再进行匹配,这样程序的效率更高。例如下面这样:
import re
s = "当月最后一天:2019-12-31"
pattern = re.compile(r"\d+") # 编译成模式对象,以后可以多次使用
r = pattern.findall(s)
print(r)
s2 = "我的生日是:1990-12"
print(pattern.findall(s2))
?
表示前一个字符匹配 0 或 1 次\.?
表示匹配小数点(.
)0 次或 1 次。注意要叫一个反斜杠,表示后面的是小数点,而不是正则中的.
。import re
s = "The most popular tools for Python development are PyCharm and VScode. The combined share of the PyCharm Community and Professional editions is 33%. And VS Code has experienced rapid growth. It started with 7.1% in 2017 and achieved 24% in 2019."
pattern = re.compile(r"\d+\.?\d*") # \d+表示至少一个数字,\d*表示0个、1个或多个数字
r = pattern.findall(s)
print(r)
import re
s = [-16, 1.5, 11.43, 10, 5, 0]
pattern_1 = r"^\d*$" # 匹配到[10, 5, 0],多了0
print([i for i in s if re.match(pattern_1, str(i))])
pattern_2 = r"^[1-9]\d*" # 匹配到 [1.5, 11.43, 10, 5],多了小数点,理解$,完全匹配的作用
print([i for i in s if re.match(pattern_2, str(i))])
pattern_2 = r"^[1-9]\d*$" # 正确
print([i for i in s if re.match(pattern_2, str(i))]) # 正则只能用到字符串上,所以str()
import re
s = "Selenium automates browsers. That's it!"
pattern = re.compile(r't')
print([i.span() for i in pattern.finditer(s, re.I)]) # [(11, 12), (15, 16), (32, 33), (37, 38)]
如果一个规则简单的字符串,直接使用字符串,split 函数。
如下requests入门文档网址字符串,可以很方便通过split方法根据分割符 / 进行分割:
s = 'https://requests.readthedocs.io/en/master/user/quickstart/'
s.split('/') # ['https:', '', 'requests.readthedocs.io', 'en', 'master', 'user', 'quickstart', '']
对于分隔符很复杂的字符串,split 函数就无能为力。这时就要用正则分割了。通过包含多个空格的句子中,单词个数,就用这个方法。
import re
s = 'This,,, module ; \t provides|| regular ; ' # 这里的分割符有, 空白(空格和制表符\t),|
pattern = r'[,\s;|]+' #多个分割符放到[]括号中,+表示1个或多个前面的那些分割符,\s表示空白字符
words = re.split(pattern, s)
print(words)
正则模块,sub 方法,替换匹配到的子串:
import re
content = "Life is short, I love Python, Python is the best programming language!"
pattern = re.compile('Python') # 编译模式串
substitution = pattern.sub('PHP', content) # 替换
print(substitution)
如果要用同一匹配模式,做很多次匹配,可以使用 compile 预先编译串。
案例:从一系列字符串中,挑选出所有正浮点数。
import re
s = [-16, 'good', 1.5, 0.2, -0.1, '11.43', 10, '5e10']
rec = re.compile(r'^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$')
print([i for i in s if rec.match(str(i))]) # 直接使用 rec,匹配列表中的每个元素,不用每次都预编译正则表达式,效率更高。
现在想要在网页中提取 div
标签中的内容,需要用到括号()。
import re
content = """正则表达式谈贪婪模式与非贪婪模式 贪婪模式非贪婪模式"""
pattern_1 = r"(.*)" # 贪婪模式,尽量多吃
r = re.findall(pattern_1, content) # ['贪婪模式
使用一对 () 去获取我们想要的部分。
(.*) 表示捕获任意多个字符,尽可能一次性地匹配更多字符,也被称为贪婪模式。尽量匹配更长的结果。
(.*?)被称为非贪婪模式。尽量匹配更多的结果。
import re
s = "https://httpbin.org/get?key1=value1 &key2=value2& key2=value3"
pattern = re.compile(r"[?&\s]+")
print(pattern.split(s)[1:])
如何判断一个URI是一个图片的地址,我们假设以http或者https开头,以合法的图片后缀名结尾的URI就是合法的图片地址。
比如https://requests.readthedocs.io/zh_CN/latest/_static/requests-sidebar.png就是合法的图片地址,因为是以png图片格式结尾的。https://farm5.staticflickr.com/4259/35163667010_8bfcaef274_k_d.jpg也是。
def is_network_picture(uri):
if uri.endswith((".png", ".jpg", ".svg")) and uri.startswith(("http", "https")):
return True
else:
return False
import re
s = "urllib3 [required: <1.23,>=1.21, installed: 1.22]"
pattern = re.compile(r'(\d+.\d+)')
print(pattern.findall(s))
import re
s = "pipenv is released at 5/28/2020"
pattern = re.compile(r'(\d+)/(\d+)/(\d+)') # 三个匹配组,第一个是月,第二个是日子,第三个是年份
print(pattern.sub(r'\3-\1-2', s)) # 反斜杠加数字形式,表示匹配组的第一个,例如\3表示匹配组的第三个
with open(filename) as f:
for line in f:
line.strip()
s = "Requests is an elegant and simple HTTP library for Python"
print(" ".join(s.split(' ')[::-1]))
len(s.split(' ')[-1])
first_str = "What you do with that power is entirely up to you."
second_str = "aeio"
for s in second_str:
for f in first_str:
if s == f:
first_str = first_str.replace(s, "")
print(first_str)
f(s)
,其中传入参数 s
是一个非空字符串;该函数的功能是统计 s
中(按字典序比较)最小字母的出现频次。def count_smallest_char(s: str) -> int:
s = sorted(s)
return s.count(s[0])
' '.join(x.capitalize() for x in s.split())
可以参考Python内置的string.py模块中的capwords函数的实现。