【Python编程入门】Lecture 10:文件和异常

本文详细介绍了Python中读写文件的步骤,包括文件路径、打开文件、读取文件和写入文件的方法。同时,讲解了异常处理的概念,如try-except代码块的使用,以及如何处理文件未找到或除零错误等常见异常。此外,还探讨了JSON在Python中的应用,用于序列化和反序列化数据。最后,通过示例展示了如何利用json模块将数据保存到和加载自.json文件。
摘要由CSDN通过智能技术生成

10.1 读写文件

10.1.1 文件路径

文件路径的表示有两种方法:绝对路径表示法和相对路径表示法。
1、绝对文件路径
绝对文件路径是指文件在硬盘上存储的真正路径。它不会被更改,除非文件在硬盘中的位置发生改变。

表示格式:‘<硬盘符>/<目录1>/<目录2>…/<文件名>’

例:‘C:/path/to/file.txt’

绝对路径往往较长,可以把它赋给一个变量。

file_path = 'C:/path/to/file.txt'

2、相对文件路径
相对文件路径是指从当前文件位置出发,指向目标文件的路径。它是目标文件与当前文件的相对位置,即使目标文件的位置不变,它也会随着当前文件位置改变而改变。

表示格式:‘./<目录1>/<目录2>…/<文件名>’

注意:
./ 表示当前目录,可以省略
../ 表示父级路径,即当前路径所在的上一级路径。(仅了解)

例:
①若当前项目在path文件夹中,则file的相对路径为’./to/file.txt’或’to/file.txt’。
②若当前项目在to文件夹中,即与’file.txt’在同一文件夹,则file的相对路径就是文件名’file.txt’。
③若当前项目在to文件夹中,需要使用的文件’file2.txt’在C盘根目录下,则file2的相对路径为’../../file2.txt’。(仅了解)

使用相对路径时,也可以把它赋给一个变量。

file_path = 'to/file.txt'

总注:
1、不同盘符间的文件不能用相对路径来表示,例如位于C盘下的文件无法使用相对路径来表示D盘中的文件。
2、显示文件路径时,Windows系统使用的是反斜杠(\)而非(/),但在代码中往往使用斜杠(/)。如过在代码中使用反斜杠(\),会导致Python将其理解为转义字符,从而引发错误,如果非要使用反斜杠(\),则需要先对每个反斜杠进行转义,即表示为

'C:\\path\\to\\file.txt'

10.1.2 打开文件

请前往 ituring.cn/book/2784 ,点击右侧第四栏“随书下载”,选择下载“源代码文件.zip”,里面包含后续部分所需的相关文本文件。

可以运用 "with open(…) as … "结构打开文件。这个结构包含两个要点,第一,函数open()可以接受一个参数,即文件路径,返回一个表示文件的对象,可以将其赋给一个变量供以后使用。第二,关键字with在不需要访问文件后自动将其关闭。

file_path = 'pi_digits.txt'
with open(file_path) as file_object:
    print(file_object)
<_io.TextIOWrapper name='pi_digits.txt' mode='r' encoding='cp936'>

我们可以看到,这样打印的并不是文件内容,而是关于文件的信息,但这也说明我们成功打开了文件。

注:其实,也可以调用open()函数和close()函数来打开和关闭文件,但是如果程序存在bug导致close()函数未执行,文件将不会被关闭,这可能会导致数据受损或丢失。另外,如果过早的调用close()函数,会导致需要使用文件时发现文件已经被关闭的情形发生。因此,不要使用这种方法。

10.1.3 读取文件

↓ pi_digits.txt

3.1415926535 
  8979323846 
  2643383279

这是一个4行的文件,包含小数点后30位圆周率,且小数点后每10位处进行一次换行。

1、读取整个文件(read()方法)
方法read()可以读取文件内容,并返回一个长长的字符串。需要注意的是,使用关键字with的时候,open()函数返回的文件只在with代码块内可用,如果要在代码块外访问文件的内容,可以将文件读取后存储在变量中,方便关闭文件后继续使用文件的内容。

file_path = 'pi_digits.txt'
with open(file_path) as file_object:
    content = file_object.read()  #此处把文件读取后存储在变量content中
print(content)
3.1415926535 
  8979323846 
  2643383279

输出末尾出现了一个空行,可以使用rstrip()方法删除字符串末尾的空白。

file_path = 'pi_digits.txt'
with open(file_path) as file_object:
    content = file_object.read()
print(content.rstrip())
3.1415926535 
  8979323846 
  2643383279

复习:strip()、lstrip()、rstrip(),可以含有一个字符类型的参数,缺省默认为空格;strip():删除string字符串开头和末尾的指定字符,lstrip():删除string字符串开头的指定字符(默认为空格),rstrip():删除string字符串末尾的指定字符(默认为空格)。

2、逐行读取
读取文件时,常常需要检查其中的每一行,可能要在文件中查找特定信息,也可能要以某种方式修改文件中的文本。要以每次一行的方式检查文件,可以对文件对象使用for循环。

file_path = 'pi_digits.txt'
with open(file_path) as file_object:
    for line in file_object:
        print(line)
3.1415926535 

  8979323846 

  2643383279
  

可以看到打印的结果中,空白行变多了,这是因为在文件中每行的末尾都有一个看不见的换行符,因此每行打印的结果都会多出一个空行。同样,可以使用rstrip()方法删除这些空行。

file_path = 'pi_digits.txt'
with open(file_path) as file_object:
    for line in file_object:
        print(line.rstrip())
3.1415926535
  8979323846
  2643383279

3、逐行读取并存储为列表(readlines()方法)
与1中类似,需要在with代码块内将文件的各行存储在一个列表中。与1、2中类似,可以结合使用rstrip()方法。

file_path = 'pi_digits.txt'
with open(file_path) as file_object:
    lines = file_object.readlines()
for line in lines:
    print(line.rstrip())
3.1415926535
  8979323846
  2643383279

【实例】圆周率中包含你的生日吗?
本例使用下载的 pi_million_digits.txt 文件,其中包含圆周率的前1000000位。

首先将文件按行储存在列表中,再将列表拼接成整洁的string类型的圆周率,检查字符串的长度是否为1000002。

file_path = 'pi_million_digits.txt'
with open(file_path) as file_object:
    lines = file_object.readlines()
pi_string = ''
for line in lines:
    pi_string += line.strip()
print(f"圆周率小数点后50位为:{pi_string[0:52]}")
print(len(pi_string))
圆周率小数点后50位为:3.14159265358979323846264338327950288419716939937510
1000002

然后提示用户输入生日,再检查用户的生日是否在圆周率中,打印相关结果。

birthday = input("请以yymmdd的格式输入你的生日:")
if birthday in pi_string:
    print("圆周率中包含你的生日")
else:
    print("小数点后一百万位圆周率中不包含你的生日")
请以yymmdd的格式输入你的生日:960518
圆周率中包含你的生日

10.1.4 写入文件

之前介绍了用于打开文件的open()函数,实际上它可以接受两个参数,第一个参数就是前面一直在用的文件路径,第二个参数是告诉Python以怎样的模式打开文件。打开文件的模式包括

模式名称参数功能文件不存在的处理方式写入方式
读取模式r只能读取报错——
r+可读可写报错覆盖
写入模式w只能写入创建覆盖
w+可读可写创建覆盖
附加模式a只能写入创建追加
a+可读可写创建追加

注:
①类似的参数还有‘rb’、‘wb’,它们分别指的是以二进制方式读取、写入。
②写入文本时,需要结合使用文件对象的write()方法。
③Python只能将字符串写入文件中,如果要将数值存储到文本文件中,必须先使用函数str()将其强制转换为字符串格式。

下面举例说明写入模式和附加模式的应用。首先尝试写入模式下,创建一个txt文件,写入一句话。

with open('new_text.txt','w') as file_object:
    file_object.write("I love programming.")

这个程序没有输出,但是我们可以在程序所在位置发现新创建的txt文件,并且写入了相关内容。
在这里插入图片描述
下面尝试在这个文件中写入两句话,

with open('new_text.txt','w') as file_object:
    file_object.write("I really love programming.")
    file_object.write("I also love creating new games")

在这里插入图片描述
可以看出,第一,新的内容覆盖了原有内容;另外,两行内容挤在一起,需要添加转义字符"\n"进行换行。

with open('new_text.txt','w') as file_object:
    file_object.write("I really love programming.\n")
    file_object.write("I also love creating new games.\n")

在这里插入图片描述
如果我们想在这个文件后,添加一些内容,则可以使用附加模式。

with open('new_text.txt','a') as file_object:
    file_object.write("I love creating apps that can run in a browser.\n")

在这里插入图片描述

10.2 异常

异常是Python创建的特殊对象,用于管理程序运行时出现的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果编写了处理异常的代码,则程序将继续运行,否则程序将停止运行并显示包含有关异常报告的traceback。

常见的异常举例:
1、ZeroDivisionError
当运算中出现0做除数时,会引发ZeroDivisionError(除零异常)。
2、FileNotFoundError
使用文件时,如果找不到文件,就会引发FileNotFoundError,这可能是文件名错误(包括后缀)、路径错误或文件不存在造成的。

10.2.1 try-except代码块

当可能发生错误时,可以编写一个try-except代码块来处理可能引发的异常。如果try代码块中的代码运行起来没有问题,则except代码块会被跳过;如果try代码块中的代码导致了错误,则Python会查找与之匹配的except代码块并运行其中的代码。

try:
    print(5/0)
except ZeroDivisionError:
    print("0不能做除数")
0不能做除数

可以看到,本例中,try代码块中的代码引发了ZeroDivisionError异常,并且存在与之对应except代码块,因此Python运行了该except代码块。这样,用户看到的是一条友好的错误提示,而非traceback。
另外,如果在except后面不带任何异常,则发生任何异常都将被except代码块捕获,但通常不建议这么用,因为这不能让用户识别出具体的异常信息。

10.2.2 try-except-else代码块

try-except-else代码块中,如果try代码块执行成功,则会继续执行else代码块中的内容。

print("请输入两个数,将得到它们相除的结果.\n输入'q'退出程序.")

while True:
    first_number = input("第一个数:")
    if first_number == 'q':
        break
    second_number = input("第二个数:")
    if second_number == 'q':
        break
    try:
        answer = float(first_number)/float(second_number)
    except ZeroDivisionError:
        print("零不能做除数。")
    except ValueError:
        print("请确保输入为两个数字。")
    else:
        print(answer)
请输入两个数,将得到它们相除的结果.
输入'q'退出程序.

第一个数:w

第二个数:2
请确保输入为两个数字。

第一个数:1

第二个数:2
0.5

第一个数:4

第二个数:0
零不能做除数。

第一个数:q

10.2.3 静默失败

在实际应用中,并非每次捕获到异常都要告诉用户,有时候需要程序在发生异常时保持静默。要让程序静默失败,只需要将except代码块的内容改为一个pass语句。
下面的例子中,我们尝试统计alice.txt、siddhartha.txt、moby_dick.txt、little_women.txt四个文档分别包含了多少个单词,同时要求Python在未找到文件时静默失败。事先我已经将moby_dick.txt从文件夹中删除。

def count_words(filename):
    '''计算一个文件大致包含多少个单词'''
    try:
        with open(filename,encoding='utf-8') as f: #当系统默认编码与读取文件的编码不一致时,需要指定encoding参数。
            contents = f.read()
    except FileNotFoundError:
        pass
    else:
        words = contents.split() #split()方法以空格为分隔将字符串拆分成多个部分
        num_words = len(words)
        print(f"{filename}单词数约为{num_words}")

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
    count_words(filename)

alice.txt单词数约为29465
siddhartha.txt单词数约为42172
little_women.txt单词数约为189079

注:
①上例中调用open()函数时指定了两个参数,当系统默认编码与读取文件的编码不一致时,需要指定encoding参数。
②字符串的split()方法以空格为分隔将字符串拆分成多个部分。

事实上,当Python出现FileNotFoundError出现时,except代码块中的代码仍然被执行了,只不过pass语句相当于什么都不发生。此外,pass语句还可以充当占位符,提示此处什么都没有做,并且方便在以后可以对此处的功能进行补充。

关于异常的一些总结:一般来说,经过详尽测试的代码不容易出现内部错误(如语法错误或逻辑错误),但只要程序依赖于外部因素(如用户输入、存在指定的文件、有网络链接),就可能出现异常。凭借经验可判断该在哪里包含异常处理块,以及出现错误时该向用户提供多少相关的信息。

10.3 存储数据

许多程序都需要把用户提供的信息存储在列表和字典等数据结构中,关闭程序时几乎总是要保存他们提供的信息。

10.3.1 基础知识简介

1、Python对象
Python对象包括所有Python基本数据类型,列表,元组,字典,自定义类等。但是不包括Python的字符串类型,把字符串或者文件流中的字符串转为字符串会报错。(待考证)

2、序列化与反序列化
①序列化:把Python对象转换为字节序列的过程。
②反序列化:把字节序列恢复为Python对象的过程。

3、对象序列化的主要用途:
①持久化对象:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。
②网络传输对象:在网络上传送对象的字节序列。

4、JSON简介
JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。最初JSON是为了JavaScript开发,它能够有效地提升网络传输效率,随后成为了被众多语言采用的常见格式。

5、Python中的json模块
Python中的json模块提供了一种很简单的方式来编码和解码JSON数据,json模块中主要包含四个方法:
①json.dumps()
②json.loads()
③json.dump()
④json.load()
①②是针对JSON编码的字节序列(本质是JSON字符串)使用的(补充内容)。
③④是针对.json文件而使用的(也是本章应该涉及的内容)。
①③是实现序列化的。
②④是实现反序列化的。

10.3.2 json.dumps()和json.loads()

json.dumps()用于将Python对象编码成JSON字符串(易传输)。它可以接收一个参数,即需要转换的Python对象。

json.loads()用于将已编码的JSON字符串解码为Python对象。它也可以接收一个参数,即需要转换的JSON字符串。

import json

data = [2, 3, 5, 7, 9]

json_data = json.dumps(data)
print(json_data)
print(type(json_data))
data2 = json.loads(json_data)
print(data2)
print(type(data2))
[2, 3, 5, 7, 9]
<class 'str'>
[2, 3, 5, 7, 9]
<class 'list'>

10.3.3 json.dump()和json.load()

json.dump()用于将Python对象编码成JSON字符串并储存到.json文件中(易存储)。它可以接收两个参数,需要存储的Python对象和用于存储数据的文件对象。

json.load()用于将.json文件解码为Python对象。它可以接收一个参数,即需要解码的.json文件。

import json

data = [2, 3, 5, 7, 9]

filename = 'data.json'
with open(filename,'w') as f:
    json.dump(data,f)

可以看到程序所在文件夹中已经有了相关的文件。
在这里插入图片描述

import json

data = [2, 3, 5, 7, 9]

filename = 'data.json'
with open(filename) as f:
    data2 = json.load(f)
print(data2)
print(type(data2))
[2, 3, 5, 7, 9]
<class 'list'>

注:简单记忆为带"s"是针对JSON字符串(str)使用的。

注:本节书上还有一个保存用户输入的例子,难度不大,从略;最后提了一下函数重构,我认为不属于此处的内容,从略。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值