字符串和正则

更多:【Python3学习笔记】之【Python基础——字符串】【Python3学习笔记】之【Python高级——正则表达式方法】【Python3学习笔记】之【Python高级——正则表达式对象】

1 反转字符串

st = 'python'
# 方法一
print(''.join(reversed(st)))
# 方法二
print(st[::-1])

2 字符串切片操作

字符串切片操作——查找替换3或5的倍数

>>> [str("java"[i%3*4:]+"python"[i%5*6:] ori) foriinrange(1,15)]
['1','2','java','4','python','java','7','8','java','python','11','java','13','14']

3 join串联字符串

lst = ['1', 'python', '2', 'java', '3', 'c']
print(','.join(lst))
# 1,python,2,java,3,c

4 字符串的字节长度

myStr = '你好'
print(len(myStr.encode('utf-8')))
# 6

5 查找第一个匹配串

import re

MyStr = 'i love python forever!'
pat = 'python'
print(re.search(pat, MyStr))
# <re.Match object; span=(7, 13), match='python'>

6 查找所有字符的的索引

import re

MyStr = 'i love python forever!'
pat = 'o'
r = re.finditer(pat, MyStr)
for i in r:
    print(i)
# <re.Match object; span=(3, 4), match='o'>
# <re.Match object; span=(11, 12), match='o'>
# <re.Match object; span=(15, 16), match='o'>

7 \d 匹配数字[0-9]

import re

string = '不要998!只要99!'
pat = r'\d+'  # +表示匹配数字(\d表示数字的通用字符)1次或多次
r = re.findall(pat, string)
print(r)
# ['998', '99']

8 匹配浮点数和整数

import re

string = '不要998!只要9.9!'
pat = r'\d+\.?\d+|\d+'  # ?表示匹配小数点(\.)0次或1次,A|B,匹配A失败才匹配B
r = re.findall(pat, string)
print(r)
# ['998', '9.9']

9 ^匹配字符串的开头

import re

string1 = 'hello'
string2 = 'apple'
string3 = 'email'
pat = r'^[abcde].*'  # 查找以字符a,b,c,d或e开始的字符串
r1 = re.findall(pat, string1)
r2 = re.findall(pat, string2)
r3 = re.findall(pat, string3)
print(r1)
print(r2)
print(r3)
# []
# ['apple']
# ['email']

10 re.I 忽略大小写

import re

string1 = 'apple'
string2 = 'Apple'
pat = r'^[abcde].*'  # 查找以字符a,b,c,d或e开始的字符串
r1 = re.findall(pat, string1, re.I)
r2 = re.findall(pat, string2, re.I)
print(r1)
print(r2)
# ['apple']
# ['Apple']

11 理解compile的作用

如果要做很多次匹配,可以先编译匹配串:

import re

pat = re.compile(r'\w+')  # \W 匹配不是数字和字母的字符
target1 = pat.findall('1!2@3#4$abc')
target2 = pat.search('1!2@3#4$abc')

if target1:
    print(target1)
# ['1', '2', '3', '4', 'abc']

if target2:
    print(target2.group(0))
# 1

12 使用()捕获单词,不想带空格

使用()捕获

import re

s = 'i love python'
pat = r'\s?([a-zA-Z]+)'  # \s表示匹配任意空白字符,括号表示要提取的内容
r = re.findall(pat, s)
print(r)
# ['i', 'love', 'python']

13 split分割单词

使用以上方法分割单词不是简洁的,仅仅是为了演示。分割单词最简单还是使用split函数。

import re

s = 'i love python'
pat = r'\s'
r = re.split(pat, s)
print(r)
# ['i', 'love', 'python']

14 match与search

注意match,search等的不同:

import re

s = 'Hello World!'
pat = re.compile('World')
print(pat.match(s))  # match只从起始位置匹配
# None
print(pat.search(s))  # search会搜索整个字符串,
# <re.Match object; span=(6, 11), match='World'>


# 两者匹配成功都是返回的一个match对象

15 替换匹配的子串

sub函数实现对匹配子串的替换

import re

s = 'i am 123,he is 456'
pat = re.compile(r'\d+')  # 要替换内容的正则,这里为数字
r = pat.sub('pig', s)  # 要替换成的内容,与目标字符串
print(r)
# i am pig,he is pig

16 贪心捕获

(.*)表示捕获任意多个字符,尽可能多的匹配字符

import re

s = r'<h>abc</h><div>def</div>ghi<div>jkl</div>'
pat = re.compile(r'<div>(.*)</div>')  # 贪婪模式
r = pat.findall(s)
print(r)
# ['def</div>ghi<div>jkl']

非贪心捕获

仅添加一个问号(?),得到结果完全不同,这是非贪心匹配,通过这个例子体会贪心和非贪心的匹配的不同。

import re

s = r'<h>abc</h><div>def</div>ghi<div>jkl</div>'
pat = re.compile(r'<div>(.*?)</div>')  # 非贪婪模式
r = pat.findall(s)
print(r)
# ['def', 'jkl']

18 常用元字符总结

字符

含义

.

匹配任意字符

^

匹配字符串开始位置

$

匹配字符串中结束的位置

*

前面的原子重复0次、1次、多次

?

前面的原子重复0次或者1次

+

前面的原子重复1次或多次

{n}

前面的原子出现了 n 次

{n,}

前面的原子至少出现 n 次

{n,m}

前面的原子出现次数介于 n-m 之间

()

分组,需要输出的部分

19 常用通用字符总结

字符

含义

\s

匹配空白字符

\w

匹配任意字母/数字/下划线

\W

和小写 w 相反,匹配任意字母/数字/下划线以外的字符

\d

匹配十进制数字

\D

匹配除了十进制数以外的值

[0-9]

匹配一个0-9之间的数字

[a-z]

匹配小写英文字母

[A-Z]

匹配大写英文字母

20 密码安全检查

密码安全要求:1)要求密码为6到20位; 2)密码只包含英文字母和数字

import re

s1 = 'asdf!@'
s2 = 'asd123'
pat = re.compile(r'[\da-zA-Z]{6,20}')
r1 = pat.findall(s1)
r2 = pat.findall(s2)
print(r1)
print(r2)
# []
# ['asd123']

选用最保险的fullmatch方法,查看是否整个字符串都匹配:

import re

s1 = 'asdf!@'
s2 = 'asd123'
pat = re.compile(r'[\da-zA-Z]{6,20}')

r1 = pat.fullmatch(s1)
r2 = pat.fullmatch(s2)
print(r1)
print(r2)
# None
# <re.Match object; span=(0, 6), match='asd123'>

21 爬取百度首页标题

import re
from urllib import request

# 爬取首页内容
data = request.urlopen('http://www.baidu.com').read().decode()

# 标题正则
pat = r'<title>(.*?)</title>'

r = re.search(pat, data)
print(r.group())
# <title>百度一下,你就知道</title>

22 批量转化为驼峰格式(Camel)

分析过程:

# \s 指匹配: [ \t\n\r\f\v]
# A|B:表示匹配A串或B串
# re.sub(pattern, newchar, string):
# substitue代替,用newchar字符替代与pattern匹配的字符所有
# title(): 将单词首字母转化为大写,例子:
# 'Hello world'.title() # 'Hello World'

初步尝试:

import re

s = 'some_database_file_name'
pat = r'(\s|_|-)+'
r = re.sub(pat, ' ', s)  # some database file name
r = r.title().replace(' ', '')  # SomeDatabaseFileName
r = r[0].lower() + r[1:]
print(r)
# someDatabaseFileName

批量转化:

import re


# 转换方法
def camel(s):
    r = re.sub(r'(\s|_|-)+', ' ', s).title().replace(' ', '')  # some database file name
    return r[0].lower() + r[1:]


# 批量转换
def database_lst_to_camel(lst):
    return [camel(s) for s in lst]


database_names = ['student_id', 'student\tname', 'student-add']
print(database_lst_to_camel(database_names))
# ['studentId', 'studentName', 'studentAdd']

23 str1是否为str2的permutation

排序词(permutation):两个字符串含有相同字符,但字符顺序不同。

from collections import defaultdict


def is_permutation(str1, str2):
    if str1 is None or str2 is None:
        return False
    if len(str1) != len(str2):
        return False
    unq_s1 = defaultdict(int)
    unq_s2 = defaultdict(int)
    # 将每个字母计数
    for c1 in str1:
        unq_s1[c1] += 1
    for c2 in str2:
        unq_s2[c2] += 1

    return unq_s1 == unq_s2


s1 = 'nice'
s2 = 'cine'
s3 = 'eleven+two'
s4 = 'twelve+one'
print(is_permutation(s1, s2))  # True
print(is_permutation(s3, s4))  # True
print(is_permutation(s1, s3))  # False

当然,方法仅供学习,更简单的方法可以直接用:

from collections import Counter


# 检查两个字符串是否相同字母异序词,简称:互为变位词
def anagram(str1, str2):
    return Counter(str1) == Counter(str2)


anagram('eleven+two', 'twelve+one')  # True 这是一对神器的变位词
anagram('eleven', 'twelve')  # False

24 str1是否由str2旋转而来

stringbook旋转后得到bookstring,写一段代码验证str1是否为str2旋转得到。

  • 思路:
    转化为判断:str1是否为str2+str2的子串
def is_rotation(s1, s2) -> bool:
    if s1 is None or s2 is None:
        return False
    elif len(s1) != len(s2):
        return False

    return s1 in s2 * 2


print(is_rotation('stringbook', 'bookstring'))  # True
print(is_rotation('stringbook', 'koobstring'))  # False

25 正浮点数

从一系列字符串中,挑选出所有正浮点数。

  • 思路:正则
^[1-9]\d*\.\d*$

^表示字符串开始
[1-9]表示数字1,2,3,4,5,6,7,8,9
^[1-9]连起来表示以数字1-9作为开头
\d表示一位0-9的数字
*表示前一位字符出现 0 次,1 次或多次
\d*表示数字出现 0 次,1 次或多次
\.表示小数点
$表示字符串以前一位的字符结束

但是这样只能匹配大于1.0的浮点数,因为当数字为000.2时,他也能匹配到,可是它不是浮点数。所以还需要写0.0到1.0之间的浮点数:

^0\.\d*[1-9]\d*$

综合:

^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$