Python笔记7 正则表达式与JSON

JSON是一种轻量级的数据格式,web数据交换的主流数据结构,相应的XML用的较少

初识正则表达式 

是一个特殊的字符序列,用于检测一个字符串是否与我们所设定的字符序列相匹配

快速检索文本、实现一些替换文本的操作,比如如下的需求:

  1. 检查一串数字是否是电话号码
  2. 检测一个字符串是否为邮箱地址
  3. 把一个文本的指定单词替换为另外一个单词

Python自带函数处理字符串:判断字符串a中是否含有Python

a = 'C|C++|Python|Java'    #判断字符串a中是否含有Python
print(a.index('Python') > -1)

print('Python' in a)

#True
#True

使用正则判断:

import re        #引入正则的包

a = 'C|C++|Python|Java'

r = re.findall('Python', a)    #findall函数返回一个列表,r = ['Python']
if len(r) > 0:
    print('字符串中包含Python')

#字符串中包含Python

这个例子中需要匹配的是 常量Python,可以说使用正则是没有意义的。下一节我们讨论有意义的正则。

元字符与普通字符 

取出给定字符串中的数字:

import re

a = 'C1C++2Python3Java4Javascript'

r = re.findall('\d', a)     #/d 表示数字0-9
print(r)

#['1', '2', '3', '4']

'Python' 普通字符,‘\d’ 元字符,正则表达式就是由一系列普通字符和元字符组成的。

取出给定字符串中的非数字:

import re

a = 'C1C++2Python3Java4Javascript'

r = re.findall('\D', a)     #/D 表示非数字
print(r)

#['C', 'C', '+', '+', 'P', 'y', 't', 'h', 'o', 'n', 'J', 'a', 'v', 'a', 'J', 'a', 'v', 'a', 's', 'c', 'r', 'i', 'p', 't']

字符集

a和c作为普通字符,可以在长字符串中确定一个小段,定界

#字符集
import re
s = 'abc, adc, aec, ahc, afc, acc'

r = re.findall('a[cf]c', s)     #中间是c或者f
print(r)

r1 = re.findall('a[^cf]c', s)    #中间不是c和f
print(r1)

r2 = re.findall('a[b-d]c', s)    #中间按照顺序是b,c,d
print(r2)

#['afc', 'acc']
#['abc', 'adc', 'aec', 'ahc']
#['abc', 'adc', 'acc']

中括号[ ] :表示 c 或者 f

^ :取反操作

减号 - : 按照顺序 b-d 表示 b,c,d,省略中间操作

概括字符集

例如之前用的 \d 表示 [0-9],两者可以替换

\D 可用 [^0-9] 替换

\w :匹配单词字符(包括下划线在内),可用 [A-Za-z0-9_]替换:

#概括字符集

import re
a = '#&*python1111java678php__'

r = re.findall('\w', a)    #包括下划线
print(r)

#['p', 'y', 't', 'h', 'o', 'n', '1', '1', '1', '1', 'j', 'a', 'v', 'a', '6', '7', '8', 'p', 'h', 'p', '_', '_']

\W :匹配非单词字符

\s:匹配空白字符,空格、\n、\r等:

#概括字符集

import re
a = '#&*python1111\tjava\r 678php__\n'

r = re.findall('\s', a)
print(r)

#['\t', '\r', ' ', '\n']

\S :匹配处空格符之外的字符

. (点):匹配除换行符 \n 之外的其他所有字符

数量词

大括号{ } 匹配连续三个字母的单词:

#数量词

import re
a = 'python1111java678php'

r = re.findall('[a-z]{3}', a)
print(r)

#['pyt', 'hon', 'jav', 'php']        #这里注意java后面的a不会匹配

匹配完整的单词:

import re
a = 'python1111java678php'

r = re.findall('[a-z]{3,6}', a)    #表示3-6个字符
print(r)

#['python', 'java', 'php']

为什么这里在匹配到3个字符时已经算是匹配完成了,却还是匹配出了python?答案请看下一节

贪婪与非贪婪 

默认情况下,Python倾向于使用一种贪婪的匹配方式(3-6的区间,匹配完成3个时,会继续向下匹配,直到取到符合情况的最大值)

非贪婪模式:

import re
a = 'python1111java678php'

r = re.findall('[a-z]{3,6}?', a)
print(r)

#['pyt', 'hon', 'jav', 'php']

{3,6}? 后面加问号;与 {3} 的匹配结果相同

匹配0次1次或者无限多次 

* 对前一个字符匹配0次或者无限次

import re
a = 'pytho0python1pythonn2'

r = re.findall('python*', a)
print(r)

#['pytho', 'python', 'pythonn']

+ 对前一个字符匹配一次或者无限多次

import re
a = 'pytho0python1pythonn2'

r = re.findall('python+', a)
print(r)

#['python', 'pythonn']

对前一个字符匹配0次或者1次

import re
a = 'pytho0python1pythonn2'

r = re.findall('python?', a)
print(r)

#['pytho', 'python', 'python']

{3,6}?和 python? 时的问号用法不一样。 

边界匹配符

判断给定qq号是否是 4-8 位

import re

qq = '100000001'        #给定9位数字

r1 = re.findall('\d{4,8}', qq)        #返回8位字符,判断错误
print(r1)

r = re.findall('^\d{4,8}$', qq)        #从字符串的第一个开始匹配,直到最后一个结束匹配,返回空,判断正确
print(r)

#['10000000']
#[]

^ $  组成边界匹配符

^ 从字符串开头开始匹配

$ 从字符串末尾开始匹配

使用例子如下:

import re

qq = '100000001'

r1 = re.findall('000', qq)
print(r1)

r2 = re.findall('^000', qq)    #匹配以000开头的字符
print(r2)

r3 = re.findall('000$', qq)    #匹配以000结尾的字符
print(r3)

#['000', '000']
#[]
#[]

判断字符串中是否出现三次Python:

import re

a = 'PythonPythonPythonPython'
r = re.findall('(Python){3}', a)

print(r)

#['Python']

这里一个括号对应一组,(Python){3} 是把Python 重复三次,Python{3} 是将前一个字符重复三次

中括号 [ ] :或关系

小括号 ():且关系

匹配模式参数

re.findall() 函数的第三个参数为:匹配模式参数

多个模式可以用 进行连接,表示且关系

忽略大小写匹配:

import re

a = 'PythonC#JavaPHP'
r = re.findall('c#', a, re.I)

print(r)

#['C#']

这里如果不加参数 re.I, 输出为空, re.I 是忽略大小写匹配

已知概括字符集 . (点):匹配除换行符 \n 之外的其他所有字符

re.S 改变点的行为,使得点可以匹配换行符 \n :

import re

a = 'PythonC#\nJavaPHP'
r = re.findall('c#.{1}', a, re.I)    #匹配c#和任意一个字符(换行符除外),输出空
print(r)

r2 = re.findall('c#.{1}', a, re.I | re.S)   #匹配c#和任意一个字符(包括换行符),可以匹配到
print(r2)

#[]
#['C#\n']

re.sub正则替换 

re.sub 替换字符串中的 C# 为 GO:

import re

a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', 'GO', a)    #默认第四个参数为0,表示将C#全部替换为GO
print(r)

r2 = re.sub('C#', 'GO', a, 3)    #第四个参数 count=3,表示字符串中最多替换3次
print(r2)

#PythonGOJavaPHPGOGOGO
#PythonGOJavaPHPGOGOC#

replace() 函数替换:

import re

a = 'PythonC#JavaPHPC#C#C#'
a.replace('C#', 'Go')        #没有替换是因为string不可变
print(a)

a = a.replace('C#', 'Go')    #重新赋值,可以实现替换
print(a)

#PythonC#JavaPHPC#C#C#
#PythonGoJavaPHPGoGoGo

replace函数可以说是 re.sub 的简单代替,第三个参数是 count

re.sub() 一个强大的功能:第二个参数传入一个函数:

import re

def convert(value):        
    pass

a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', convert, a)
print(r)

#PythonJavaPHP

这里处理过后的 C# 都消失了,原因是:re.sub()的第二个参数传入一个函数时,它的流程是:当匹配到第一个 C# 时,会把匹配的结果传入这个函数中,也就是 C# 会作为 convert函数的value传入,convert 函数的返回结果回用于替换原字符串中的 C#

因此如上例子中,convert函数并未返回,所以C# 全部消失

但是要注意,此处传入的 C# ,并不是简单的以字符串的方式传入,而是作为一个对象,打印如下:

import re

def convert(value):
    print(value)        #匹配到4次,调用4次

a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', convert, a)
print(r)

# <_sre.SRE_Match object; span=(6, 8), match='C#'>    #span表示当前匹配的字符在原字符串中的位置
# <_sre.SRE_Match object; span=(15, 17), match='C#'>
# <_sre.SRE_Match object; span=(17, 19), match='C#'>
# <_sre.SRE_Match object; span=(19, 21), match='C#'>
# PythonJavaPHP

使用对象 value 的 group 方法可以可以取出对应字符串,给匹配到的 C# 前后加上感叹号 :

import re

def convert(value):
    matched = value.group()
    return '!!' + matched + '!!'

a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', convert, a)
print(r)

#Python!!C#!!JavaPHP!!C#!!!!C#!!!!C#!!

re.sub() 传入函数非常实用,特别是在对匹配到的字符做进一步操作时

把函数作为参数传递

找出字符串中的所有数字,大于等于 6 时替换为 9,小于 6 替换为 0:

import re
a = 'A8C3467290'

def convert(value):
    matched = value.group()     
    if int(matched) >= 6:       #注意字符串转为 int
        return '9'              #返回值必须是string
    else:
        return '0'

r = re.sub('\d', convert, a)    #每找到一个数字,就调用convert一次
print(r)

#A9C0099090

这种函数调用函数的方式非常灵活

练习:传入的字符串字母和数字夹杂,数字分为一位和两位,要求剔除所有一位的数字,剩下的大于等于50 替换为99,小于50 替换为0:

import re
a = 'A83C12D1D8E67'

def convert(value):
    matched = value.group()
    if int(matched) >= 50:
        return '99'
    elif int(matched) < 10:
        return ''
    else:
        return '0'

r = re.sub('\d{1,2}', convert, a)    #匹配一到两位数字
print(r)

#A99C0DDE99

search与match函数

区别与联系:

  1. match 从字符串的开始就要匹配
  2. 搜索需要匹配的字符串,直到找到第一个满足需要的字符串,把这个对象返回回来
  3. 都会在匹配到第一个时,就停止搜索,不会继续往下匹配
  4. match 和 search 两者返回的均是对象,但是 findall 更为简便,返回的是匹配到的 列表,推荐使用 findall
import re
a = 'A83C12D1D8E67'

r = re.match('\d', a)    #第一个字符不是数字,返回None
print(r)

r2 = re.search('\d', a)
print(r2)

b = '1234'        #第一个字符改为数字,可返回一个对象
r3 = re.match('\d', b)
print(r3)

#None
#<_sre.SRE_Match object; span=(1, 2), match='8'>
#<_sre.SRE_Match object; span=(0, 1), match='1'>

可以看出,match 和 search 两者返回的均是对象,但是 findall 更为简便,返回的是匹配到的 列表

group() 函数 和 span() 函数:

import re

a = 'A83C12D1D8E67'
r2 = re.search('\d', a)
print(r2.group())    #返回对象中的字符
print(r2.span())     #返回字符的位置

b = '1234'
r3 = re.match('\d', b)
print(r3.group())
print(r3.span())

# 8
# (1, 2)
# 1
# (0, 1)

group分组

提取 life 和 python 之间的字符:

import re

a = 'life is short, i use python'
r = re.search('life(.*)python', a)  
#.* 表示除换行符以外的字符(包括空格)重复0到无限次


print(r.group(1))

# is short, i use

对于 'life.*python' 如果不加括号,默认为  '(life.*python)' ,即默认是一个组

group函数可以传入参数,表示要获取的组号,当传入 group(0) 时就是默认情况,即获取完整匹配结果

但是要访问完整匹配结果内部的分组的话,必须从 1 开始访问;因此这里使用 'life(.*)python',对 .* 加括号,group(1) 就可以获取到中间的字符

用search 完成有些难懂,使用 findall 更为简便:

import re

a = 'life is short, i use python'
r = re.findall('life(.*)python', a)
print(r)

#[' is short, i use ']

这在Python爬虫书写中,读取HTML标签中间数据中很常用

更加复杂的字符串处理,返回 life 和 python 之间、python 和 python 之间的字符:

import re

a = 'life is short, i use python, i love python'
r = re.search('life(.*)python(.*)python', a)        #两个组

print(r.group(0))
print(r.group(1))
print(r.group(2))

print(r.group(0,1,2))    #以元祖形式返回三个结果
print(r.groups())        #返回两个组

# life is short, i use python, i love python
#  is short, i use
# , i love
# ('life is short, i use python, i love python', ' is short, i use ', ', i love ')
# (' is short, i use ', ', i love ')

一些关于学习正则的建议

python绝大多数用在爬虫上,需要用到正则表达式

搜索'常用正则表达式'并加以分析

理解JSON

JavaScript Object Notation 的简写:JavaScript 对象标记

本质上,JSON 是一种轻量级的数据 交换格式

  1. 与 XML 相比较,JSON 是轻量级的
  2. 注意 JSON 是一种数据格式

字符串 是JSON的表现形式

符合JSON 格式的字符串叫做JSON字符串,例如 {"name" : "julia"} 就是标准的 JSON 格式,和python中的字典对应

JSON 优势:

  1. 易于阅读
  2. 易于解析
  3. 网络传输效率高
  4. 适合于跨语言交换数据

JSON 的应用场景:

简单的网站web场景下:

较为复杂的移动端场景:

我们要理解,JSON 本身只是一种语言格式,它是一种格式、规范,并不是实实在在的东西;JSON 的载体是JSON字符串;并且 JSON 并不是某一个语言所特有的,可以作为各个语言之间的数据交互。

反序列化

利用python 内部的 json 解析JSON 数据:

import json

json_str = '{"name":"julia", "age":18}'    #JSON字符串

student = json.loads(json_str)

print(type(student))
print(student)
    
print(student['name'])        #访问数据项
print(student['age'])

#<class 'dict'>
#{'name': 'julia', 'age': 18}
#julia
#18

JSON 在python中对应 字典 的格式,要注意:

  1. JSON规定内部的key必须用双引号 "julia"
  2. value为字符串时,使用双引号,int型不加双引号
  3. 这样一来,需要解析的python 字符串外部加 单引号
  4. json.load 函数解析 python字符串,解析之后变为 字典 格式

在python,对于 json 字符串会解析为字典;其他语言中不一定,对应于 json 中的数据结构对应的是 object

另外json 还有 array 类型:

import json

json_str = '[{"name":"julia", "age":18}, {"name":"julia", "age":18}]'

student = json.loads(json_str)    #load函数把json数据类型转化为python的数据类型

print(type(student))
print(student)

#<class 'list'>
#[{'name': 'julia', 'age': 18}, {'name': 'julia', 'age': 18}]

json中的数据结构 array 对应python中的列表,每一项是object(对应python中的字典)

反序列化:由字符串到某一种语言对象的解析过程

json到Python对应的数据转换类型:

序列化

序列化:python数据类型 转化为 json字符串,如下所示:

import json

student = [
    {'name':'julia', 'age':18, 'flag':False},
    {'name':'julia', 'age':20}
]

json_str = json.dumps(student)
print(json_str)
print(type(json_str))

#[{"name": "julia", "age": 18, "flag": false}, {"name": "julia", "age": 20}]
#<class 'str'>

序列化不仅仅局限于json与python之间的数据转化,还有XML与python之间

小谈JSON、JSON对象与JSON字符串

 

JSON 与 JavaScript区别(没有直接关系):

  1. JSON 并不是特定于 JavaScript的,并不是JavaScript的附属品
  2. JavaScript只是 标准ECMASCRIPT的实现方式之一,另外还有实现方式 ActionScript,我们可以理解 JSON 也是ECMASCRIPT 的实现版本之一,某种程度上JavaScript 和 JSON 可以看成平齐的语言
  3. JSON 大量应用于 JavaScript 的交互
  4. json有自己的数据类型,虽然他和JavaScript的数据类型有些相似

JSON 对象:只是放在 JavaScript当中时,JSON 对象是成立的;

JSON字符串:只放在 JavaScript当中

JSON:

REST 服务的标准形式:JSON

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值