
【千锋教育干货暴击】
如果你想更好的学习Python乃至转行,弯道超车,快人一步!本课程零基础即可加入学习,抓住大数据、机器学习、人工智能时代的红利,开启你的第一行代码吧!
↓ ↓ ↓
千锋教育Python教程_700集零基础Python入门到精通教程(保姆级新手教程)
千锋教育Python教程全套_python零基础入门到精通(学完可达到Python工程师水平)

玩游戏的小伙伴们众所周知,英雄联盟职业联赛,简称LPL(League of Legends Pro League),是中国大陆最高级别的英雄联盟职业比赛,同时也是中国大陆赛区通往每年英雄联盟季中冠军赛和英雄联盟全球总决赛的唯一渠道。
每年的LPL由春季赛和夏季赛组成,每季分为常规赛与季后赛两部分。常规赛积分排名前八的战队将晋级季后赛,为赛季总冠军以及高额的赛事奖金继续展开争夺。
今年的LPL春季赛,LPL有十七支战队。战队之间的比赛这么多,我们怎么能把这些比赛情况保存下来呢,这样我们日后做数据分析的时候也更方便。
有人想到可以通过Excel表格、word文档或者普通的记事本都可以记录的,并且持久化保存,确实是没有问题的,但是如果数据量比较大的话你记录几次就会感觉烦了。但是我们Python可以轻松的操作这些文件,使复杂的变得更加的智能化。
首先我们来了解下open()函数,在Python中,如果想要操作文件,首先需要创建或者打开指定的文件,并创建一个文件对象,而这些工作可以通过内置的 open() 函数实现。
open() 函数用于创建或打开指定文件,该函数的常用语法格式如下: file = open(file_name [, mode='r' [ , buffering=-1 [ , encoding = None ]]])
此格式中,用 [] 括起来的部分为可选参数,即可以使用也可以省略。其中,各个参数所代表的含义如下:
file_name:要创建或打开文件的文件名称,该名称要用引号括起来。需要注意的是:该文件名称可以是相对路径也可以是绝对路径。一般使用相对路径。
mode:可选参数,用于指定文件的打开模式。默认以只读(r)模式打开文件,也可以是写入(w)模式,追加(a)模式等,具体可以参照下面的表格。
buffering:可选参数,用于指定对文件做读写操作时,是否使用缓冲区。
encoding:手动设定打开文件时所使用的编码格式,不同平台的 ecoding 参数值也不同,以 Windows 为例,其默认为 cp936(实际上就是 GBK 编码)
mode模式一览表:

注意:open()函数的返回值是一个stream。更加详细的情况可以参看源码内容(按住ctrl 查看open的源码)
def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True): # known special case of open
"""
Open file and return a stream. Raise OSError upon failure.
file is either a text or byte string giving the name (and the path
if the file isn't in the current working directory) of the file to
be opened or an integer file descriptor of the file to be
wrapped. (If a file descriptor is given, it is closed when the
returned I/O object is closed, unless closefd is set to False.)
mode is an optional string that specifies the mode in which the file
is opened. It defaults to 'r' which means open for reading in text
mode. Other common values are 'w' for writing (truncating the file if
it already exists), 'x' for creating and writing to a new file, and
'a' for appending (which on some Unix systems, means that all writes
append to the end of the file regardless of the current seek position).
In text mode, if encoding is not specified the encoding used is platform
dependent: locale.getpreferredencoding(False) is called to get the
current locale encoding. (For reading and writing raw bytes use binary
mode and leave encoding unspecified.) The available modes are:
========= ===============================================================
Character Meaning
--------- ---------------------------------------------------------------
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' create a new file and open it for writing
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
'U' universal newline mode (deprecated)
========= ===============================================================
..... 我们发现除了第一个参数file之外,其余的参数都是有默认值的,使用的时候根据自己情况赋值。
只有将这个file路径给了open,open才能知道要连接访问的文件位置。如果这个位置的文件存在则会返回一个stream对象。
这个stream对象就是两个文件之间的一个通道,就类似A、B两个城市之间建造的公路,有了路才可以在两个城市之间进行内容的传送。

代码:
stream = open('electronic_sports.txt') # electronic_sports.txt是相对路径的写法
print(stream) 打印结果:
<_io.TextIOWrapper name='electronic_sports.txt' mode='r' encoding='UTF-8'> 大家可以看到如果不传递mode模式的参数,即采用的就是默认值mode = ‘r’,当然我们也可以写成“w”
stream = open('electronic_sports.txt',mode='w')
print(stream) 运行结果:

mode发生了改变,表示文件写文件。
这只是表示建好了“通行的公路”,如果想在公路上传输内容,则需要下面的操作,我们以向electronic_sports.txt文件中添加内容为例,带领大家看一下具体的文件操作。
假设我们有一场赛场记录record = '4 月 18 日 17:00 FPX PK RNG',需要将此记录保存到文件中。 那如何才能将数据保存到一个文件中呢?通过open()对文件写进行写操作。写操作就要调用用到stream对象的write方法

record = '4 月 18 日 17:00 FPX PK RNG'
stream = open('electronic_sports.txt',mode='w')
stream.write(record)
stream.close() 注意:

但是如果我们的记录是一个列表或者元组保存着多条记录呢?
大家只要记住下面的步骤,以后就可以轻松操作了。

现在我们有一些比赛数据存放于列表中如下:
records = ['4月01日 17:00 IG PK RA','4月02日 17:00 苏州LGN PK SN','4月03日 17:00 FPX PK RA','4月04日 17:00 SN PK 西安WE','4月05日 17:00 北京JDG PK FPX','4月06日 17:00 SN PK TES']
# 第一步:准备数据 records列表存放数据
records = ['4月01日 17:00 IG PK RA','4月02日 17:00 苏州LGN PK SN','4月03日 17:00 FPX PK RA','4月04日 17:00 SN PK 西安WE','4月05日 17:00 北京JDG PK FPX','4月06日 17:00 SN PK TES']
# 第二步 创建可以进行文件写的流对象
stream = open('electronic_sports.txt',mode='w')
# 第三步 使用流保存记录,遍历sports,逐行添加记录
for record in records:
stream.write(record)
# 第四步 关闭流对象
stream.close() 仔细观察结果,此时会将所有的内容都写到一行上,说明每写一次不会换行,而需要我们自己加换行符\n
# 第一步:准备数据 records列表存放数据
records = ['4月01日 17:00 IG PK RA','4月02日 17:00 苏州LGN PK SN','4月03日 17:00 FPX PK RA','4月04日 17:00 SN PK 西安WE','4月05日 17:00 北京JDG PK FPX','4月06日 17:00 SN PK TES']
# 第二步 创建可以进行文件写的流对象
stream = open('electronic_sports.txt',mode='w')
# 第三步 使用流保存记录,遍历sports,逐行添加记录
for record in records:
stream.write(record+'\n')
# 第四步 关闭流对象
stream.close() 比如我们在上面代码的基础上再加一行代码:
..... # 上面的代码省略
stream('写入完毕!') 大家就会发现有报错产生了,原因是:当stream流关闭了,就不能再次写入任何的内容了,否则就会报错。
比如现在又有的新的记录想在现有的记录基础上再追加一条记录如何实现呢?
比如:record = '4 月 18 日 17:00 FPX PK RNG' 追加到原来的上面,此时使用 mode='a'完成
record = '4 月 18 日 17:00 FPX PK RNG'
stream = open('electronic_sports.txt',mode='a')
stream.write(record)
stream.close() 大家就会发现在原有的内容后面追加了“4 月 18 日 17:00 FPX PK RNG”内容。
我们能不能通过 Python把刚才写入的内容读出来进行查看呢?
写文件的时候我们使用了write()函数,将内容写到了文件中,这次反过来了,我们需要将写进去的内容读出来。
读取文件我们使用的是stream对象的read()函数。
stream.read([字节数]) ----> 字节数是选填的,如果不填表示读取全部内容,如果填写则表示从头读取指定的的字节个数.
还是按照上面的步骤操作,只不过将写操作换成了读取的操作,用法如下:
# 创建读文件的流对象
stream = open('electronic_sports.txt',mode='r')
# content = stream.read(10) 表示读取 10 个字节的内容 ,然后演示下面的
content = stream.read() # 当然也可以读取全部内容
print(content)
# 关闭流对象
stream.close() 但是此时大家注意不能使用 mode='w',进行读操作,而是需要将mode后面的值换成"r"
同时还有一个问题需要提示大家,在写入文件的时候,如果文件不存在就会创建一个新的文件,而在读取文件的时候,如果你写的文件路径不对或者文件名有误等,则都会报错的。
# 创建读文件的流对象
stream = open('file/electronic_sports.txt',mode='r')
content = stream.read()
print(content)
# 关闭流对象
stream.close() 原因是:你并不存在file/electronic_sports.txt这样的路径和文件。
遇到这些错误的时候该如何处理呢?
使用异常处理try...except...finally 搞定
使用 with
什么是异常?
异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。异常其实是一个Python对象,表示一个错误。
当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
异常处理
异常处理方式就是:使用try/except语句捕获异常。
try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在try里捕获它。

try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
比如上面的代码:
stream=None
try:
# 创建读文件的流对象
stream = open('file/electronic_sports.txt',mode='r')
content = stream.read()
print(content)
stream.close()
except:
print('呀!有异常了,文件找不到!') 其实这个时候将stream.close()语句放到try代码块中是不很合适不严谨的,因为如果在读取的时候发生错误,则直接进入except代码块中,所以stream流就不会关闭了。所以我们要实现无论是否有异常stream都要关闭才可以。此时我们使用异常处理的另一个结构:
try/exept/finally
具体语法格式:

代码:
stream=None
try:
# 创建读文件的流对象
stream = open('file/electronic_sports.txt',mode='r')
content = stream.read()
print(content)
except:
print('呀!有异常了,文件找不到!')
finally:
if stream!=None:
stream.close() 为什么要将stream.close()放到最后呢?因为我们想保证无论是否存在异常都能释放 stream 对象。
使用 with
with 是一个关键字,with语句实质上是一个上下文管理器,可以理解为是try-finally的简写形式,但是又不是全相同。使用格式:

上面的代码该写成:
with open('electronic_sports.txt',mode='r') as stream:
content = stream.read()
print(content) 表达式open('electronic_sports.txt')返回是一个_io.TextIOWrapper 类型的变量用stream接受到。在with语句块中就可以使用这个变量操作文件。执行with这个结构之后。stream会自动关闭。相当于自带了一个finally。
但是with本身并没有异常捕获的功能,但是如果发生了运行时异常,它照样可以关闭文件释放资源。
with open('2.txt',mode='r') as stream: # 假设我们不存在2.txt文件,但是如果不存在则会报错,存在则可以读取对应的文件
content = stream.read()
print(content)
print(stream.colsed)

显示有如上的报错信息,说明with关键字没有捕获异常的功能,但是我们如何判断是否将流进行了关闭呢?
try:
with open('1.txt',mode='r') as stream: # 1.txt是存在的,里面的内容是:hello123456。
content = stream.read(5) # 读取5个字节的内容
print(content)
stream.seek(-3,0) # 设置指针的偏移,第二个参数0表示从头开始,第一个参数-3表示向左数-3
content = stream.read()
print(content)
except:
print('error')
print(stream.closed) 打印结果:
hello
error
True 说明在有报错的情况下,stream流是关闭的。在什么时候关闭的stream呢?
原理分析:

所以根据上面的原理我们知道,只要进入到with的语句块中,就执行了__enter__()方法,而如果有异常则会触发__exit__()方法或者程序如果正常结束也会自动触发__exit__()方法。
以上的原理都是with底层完成的,所以我们使用的时候知道它的原理即可。

需要资料也可以关注微信公众号:Python专栏,事不宜迟,一起进步吧!
