Python.第七章(文件与异常)

文件与异常 这章中,遇到的关于出现 UnicodeDecodeError: 'gbk' codec can't decode byte 0xa7 in position 4: illegal multibyte sequence 报错问题,程序运行后出现 \ufeff 问题都会有解释和处理办法

# 第七章

# 文件与异常


# 文件与文件类型


# 文件是一组相关数据的集合
# 组成文件的数据可以是ASCII编码,也可以是二进制编码

# 文件一定有一个文件名,文件名的长度和命名规则因不同的操作系统而异。但是无论是何种操作系统
# 文件名都包含两部分:主文件名和扩展名,两者之间用“.”隔开

#  全文件名 由用户根据操作系统的命名规则自行命名,用来与其他文件加以区别
#  扩展名 根据文件类型对应专属的缩写,用来指定打开和操作该文件的应用程序

#  例如,编写并保存的Python 语言源程序文件对应的扩展名就是.py
#  表示该文件需要用 Python 语言编辑器打开和处理

# 一般来说,文件都是按文件名访问的,一方面通过主文件名指明访问对象
# 另一方面通过扩展名指定访问和处理文件的应用程序


# 目录与文件路径

# 文件是用来组织和管理一组相关数据的,而目录是用来组织和管理一组相关文件的

# 目录又可称为文件夹,可以包含文件,也可以包含其他目录

# 文件保存的位置称为路径。

# 1.绝对路径
# 绝对路径是指从文件所在驱动器名称(又称“盘符“始描述文件的保存位置)

# 文件“5-1py”的对路径是“F盘根目下documents 目录下 python 目录下5-1.py文件”

# 具体可表示为:
# F:\documents\python\5-1.py

# 其中,反斜杠“\”是盘符、目录和文件之间在 Windows 换系统下的分隔符
# 如果要在Python 程序中描述一个文件的径,需要使用字符串
# 因为在字符串中,反斜杠“ ”是转义序列符,所以为了还原反斜杠分隔符的含义
# 在字符串中要连续写两个反斜杠如

# “F:\\documents\\python\\5-1.py”。
# 为了书写简便,Python 语言提供了另一种路径字符串的表示方法:

# r "F:\documents\python\5-1.py"

# 其中,r表示取消后续字符串中反斜杠“”的转义特性
# 在后续的代码中这两种路径字符写法会交替使用,请读者注意



# 2.相对路径


# 相对路径是指从当前工作目录开始描述文件的保存位置

# 每个运行的程序都有一个当前工作目录,又称为 cwd
# 一般来说,当前工作目录默认为应用程序的安装目录,可以通过Python语言自带的 os库函数 重新设置

# 下面的代码尝试将 cwd 从系统默认的设置修 documents 目录

import os
os.getcwd()   #查看当前工作目录
print(os.getcwd())
# E:\Python\pythonProject

os.chdir('E:\\Python\\Ptest')  #修改当前工作目录为 E盘根目录下 \Python\Ptest
print(os.getcwd())
# E:\Python\Ptest


# 一个特殊的标记“..”,它表示当前目录的上一级目录



# 文件操作


# 一般来说,文件的操作分以下三个步
# (1) 打开文件
# (2) 读文件或者写文件
# (3) 关闭文件


# 文件的打开与关闭


# 1.文件的打开

# 大多数文件都是长期保存在外部存储器的,需要操作时必须先调入内存,才能由 CPU进行处理
# 而“打开”操作就是将文件从外部存储器调入内存的过程,这个过程需要使用
# Python 语言内置的 open 命令,并生成一个 File 对象
# 具体的语法格式如下:

# file对象名 = open(文件路径字符串,模式字符)

# 其中,文件路径字符串可以采用绝对路径,也可以采用相对路径
# 打开文件的模式字符用于指定打开文件的类型和操作文件的方式
# 打开文件的模式字符: 'r','r+','w','w+','a','a+','rb','rb+','wb','wb+','ab','ab+'


# 2.文件的关闭

# 执行 open 命令打开文件后,这个文件就被 Python 程序占用并被调入内存。其后所有的读写操作都发生在内存
# 与此同时,其他任何应用程序都不能操作该文件

# 当读写操作结束后,必须将文件从内存保存回外存
# 这样做一方面为了将内存中文的变化同步至外存,以便长期保存;
# 另一方面是为了释放 Python 程序对文件的占用,让其他应用程序能够操作文件

# 这个将文件保存回外存的操作是由文件对象的close()方法实现的
# 具体的语法格式如下

# file 对象.close()

# 下面根据图7-1对应的目录结构尝试一些文件的打开操作。
# 下列代码中首先将当前工作目录设置为“python”;然后尝试用读模式打开当前目录下名
# 为“mydata.txt”的文本文件,如图 7-3 所示,该文件不存在,因此系统报错;再次尝试用写
# 模式扩开这个不存在的文件时,命令顺利执行,如图7-4 所示,系统会在“python”目录下新
# 建一个大小为“0字节”名称为“mydata.txt”的文件,并将其与文件对象 file进行关联


import os
print(os.getcwd())
# E:\Python\Ptest

'''filePractice = open('mydate.txt','r')'''

# 因为当前 E:\Python\Ptest 下没有mydate.txt这个文本文件,用 只读模式打开 会报错

# Traceback (most recent call last):
#   File "E:\Python\pythonProject\mzw.py", line 4532, in <module>
#     filePractice = open('mydate.txt','r')
#                    ^^^^^^^^^^^^^^^^^^^^^^
# FileNotFoundError: [Errno 2] No such file or directory: 'mydate.txt'


filePractice = open('mydate.txt','w')

# 用 只可写模式打开 不存在的文件,会新建
# 此时 在对应的 你的电脑里 "此电脑" 对应的 E:\Python\Ptest 下就会出现 mydate.txt 文本文件

print(filePractice)
# <_io.TextIOWrapper name='mydate.txt' mode='w' encoding='cp936'>


# 接下来关闭打开的 mydate.txt 文本文件

filePractice.close()

print(filePractice)
# <_io.TextIOWrapper name='mydate.txt' mode='w' encoding='cp936'>
# 调用了 close() 方法后,文件对象filePractice 依然与“mydata.txt”相关联


'''filePractice.write('文件已关闭')'''
# ValueError: I/O operation on closed file.
# 但是执行写操作时系统报错,不允许对关闭的文件执行任何读写操作

# 调用了 close() 方法关闭文件,文件是关闭的,但是依然与对应的文件对象相关联
# 在 Python 程序中,文件一旦打开都会和一个file对象相关联
# 随后的文件操作都通过调用file对象的方法来实现


# 写文件


# 内置命令 open 以写模式在“Ptest”目录下新建了一个新文件“mydata.txt”
# 从图看到该文件目前大小为 0,是个空文件


# 下面开始给这个文件写上内容
# 后续所有的文件操作都假设当前目录为“Ptest”


# 1.用file对象的 write()方法写文件


# write()方法将指定的字符串写入文件当前插入点位置
# 其具体的语法格式如下:

# file对象.write(写入字符串)

import os
print(os.getcwd())   # E:\Python\Ptest

'''filePractice = open('mydata.txt','w')'''


'''filePractice.write('飞雪连天射白鹿')'''
# print(filePractice.write('飞雪连天射白鹿'))
# 7

'''filePractice.write('笑书神侠倚碧鸳')'''
# print(filePractice.write('笑书神侠倚碧鸳'))
# 7

'''filePractice.close()'''


# 上面的示例代码将文件以写模式打开后,连续调用了两次 wtite() 方法,
# 写入了两个符串,每一次调用 write() 系统都会返回这次写入文件的字符数

# 从文本文件“mydata.txt”的内容中可以看出:
# (1)打开文件执行写入操作时连的 rte0方法按照顺序依次写入字符
# (2)文件的 write() 方法将指定的学符里参数原样写入文件,连续写入的不同字符间不会添加任何分隔符



# 2.用 file 对象的 writelines()方法写文件


# 与 write() 方法相比,
# writelines()方法可以以 序列 的形式接受多个字符串作为参数
# 一次性写入多个字符串

# 具体的语法格式如下:

# file 对象.writelines(字符串序列)

# 用的原文件,'w'只可写模式,会在原来的基础上是 重新覆盖写入的

filePractice=open("mydata.txt",'w')

filePractice.writelines(["飞雪连天射白鹿\t","笑书神侠倚碧鸳\n"])
filePractice.writelines (["横批:越女剑\n"])

filePractice.close()


# 下面我是新建了一个 mydata1.txt 文本文件

filePractice1=open("mydata1.txt",'w')

filePractice1.writelines(["飞雪连天射白鹿\t","笑书神侠倚碧鸳\n"])
filePractice1.writelines (["横批:越女剑\n"])

filePractice1.close()


# writelines()方法和 write()方法一样,都是字符串原样写入文件,不添加任何分隔符
# 所以只有在列表参数的每个字符串末尾加上分隔符,才能得到图所示带制表符和换行文件内容


# write()方法和 writelines()方法也可以用于追加模式('a'或者'a+'),
# 唯一的区别是追加模式写入的字符串都是从文件原来的 结尾处 开始写入,不是覆盖写入

# write()方法和 writelines()方法也可以用于追加模式('a'或者'a+'),
# 唯一的区别是追加模式写入的字符串都是从文件原来的 结尾处 开始写入,不是覆盖写入

# writelines()方法的参数除了列表之外,也可以是集合、元组,甚至是字典,但是元素一定要是字符串




# 读文件



# 1.用file对象的 read()方法读文件

# read()方法读出文件所有内容并作为一个字符串返回
# 具体的语法格式如下:

# 字符串变量 = file 对象.read()

file = open("mydata.txt",'r')

text = file.read()
print(text)
# 飞雪连天射白鹿   笑书神侠倚碧鸳
# 横批:越女剑

file.close()

# 从这里来看,同一个文件 所对应的文件对象的名字不必须是一样的,但指代的都是同一个文件

# 上述示例中,read()方法读取了“mydata.txt”文件中包括分隔符在内的所有内容,
# 并将其作为一个字符串返回赋值给了“text”变量



# 2.用 file 对象的 readline()方法读文件


# readline()方法将读出文件中 当前行 ,并以字符串的形式返回
# 具体的语法格式如下:

# 字符串变量 = file 对象.readline()

file = open("mydata.txt",'r')
text = file.readline()

print(text)
# 飞雪连天射白鹿   笑书神侠倚碧鸳

text=file.readline()
print(text)
# 横批:越女剑

file.close()


# 用读模式打开文件后,连续调用了两次readline()方法, 依次 读出了文件中的两行
# 可见,如果文件包含更多行,只需配合恰当的 for 循环也能顺利按行读出文件内容



# 3,用file 对象的readlines()方法读文件


# readlines()方法以 列表的形式 返回整个文件的内容,其中一行对应一个列表元素
# 具体的语法格式如下:

# 列表变量 = file 对象.readlines()

file = open("mydata.txt",'r')

ls = file.readlines()
print(ls)
# ['飞雪连天射白鹿\t笑书神侠倚碧鸳\n', '横批:越女剑\n']

file.close()


# 与readline()方法相比,readlines()方法以更简洁的方式按行读出了整个文件的内容
# 后续只要通过简单的列表遍历就可以取出任意一行进行处理
# CSV 文件操作


# CSV(Comma-SeparatedValues)是指号分隔值)CSV 文件可以理解为用带号分隔
# (可以是其他简单字符分割)的纯文本形式存储表格数据的文件

# CSV文件可以用记事本、写字板和 Excel打开

# 因为不带任何格式信息,所以 CSV 文件广泛应用于在不同程序之间转移表格数据

# CSV 文件的分隔符不限定为逗号,但是作为 CSV 文件,一般需要具有以下特征:
# (1)纯文本,使用某个字符集,如 ASCII、Unicode、EBCDIC或GB2312
# (2)由记录组成,一行对应一条记录,每行开头不留空格
# (3)每条记录被 英文半角分隔符 (可以是逗号、分号、制表符等)分割为多个字段
# (4)每条记录都有同样的字段序列
# (5)如果文件包含字段名,字段名写在文件第一行
# (6)不包含特殊字符,文件中均为字符串

# 针对CSV文件,Python 语言提供了内置同名模块进行读写操作
# 使用时需要定义 reader对象 和 writer对象



#  CSV文件的打开

# CSV 文件可以使用 open 命令直接打开,用法和前面介绍的一样
# 但是使用 open 命令打开的文件一定要通过调用 close()方法进行关闭
# 因此,当需要频繁进行文件操作时,使用 open 命令会显得有些烦琐

# 所以,Python 语言引入了 with 语句用来打开文件,并在文件操作结束后 自动关闭文件
# 除了语法书写上有变化,其他的与前面的的类似
# 其具体的语法格式如下:

# with open(文件路径字符串,模式字符) as 文件对象名:
#   文件操作语句


with open("mydata.txt",'r') as file:
    print(file.readline())
# 飞雪连天射白鹿   笑书神侠倚碧鸳
    print(file.readline())
# 横批:越女剑

# 使用 with 语句用读模式打开了“mydata.txt”文件后,两次调用readline()方法读出了文件中两行的内容
# 当文件读操作结束后,系统自动调用了 close()方法关闭了文件

# 从结构上来看,with 语句更清晰地描述了文件从打开到操作完毕的整个过程,使用起来也更为方便简洁
# 在后续 CSV 文件的操作中,都将使用 with 语来打开和处理文件



# reader 对象


# 使用csv模块读取CSV文件数据时需要先创建一个reader对象
# 然后通过选代遍历reader对象来遍历文件中的每一行

# 下面以读 stu.csv 文件为例来讲解reader 对象的具体使用
# import csv
# with open("stu.csv",'r') as stucsv:
#     reader = csv.reader(stucsv)
#     for row in reader:
#         print(row)

# UnicodeDecodeError: 'gbk' codec can't decode byte 0xa7 in position 4: illegal multibyte sequence
# UnicodeDecodeError:“gbk”编解码器无法解码位置 4 中的字节0xa7:非法多字节序列

# 下面分析报错的原因

# UnicodeDecodeError 通常表示你试图使用某种编码(如 'gbk')来解码
# 一个实际上是用另一种编码(如 'utf-8')编码的字节序列,而这种编码之间无法正确转换
# 在我的这种情况下,错误信息表明,尽管文件是用 'utf-8' 编码的,但是 Python 却试图用 'gbk' 编码来解码它,导致解码失败


# 这个错误通常发生在以下几种情况:
# 文件实际上是使用 'gbk' 编码的,但是你告诉 Python 用 'utf-8' 来解码它
# 文件包含一些 'utf-8' 编码无法处理的字符,但是你试图用 'utf-8' 来解码整个文件。
# 系统的默认编码不是 'utf-8',而是 'gbk' 或者其他,因此当你尝试读取文件时,它会尝试使用系统的默认编码


# 下面是改正过后的做法

import csv
with open("stu.csv", 'r', encoding='utf-8-sig') as stucsv:
    reader = csv.reader(stucsv)
    for row in reader:
        print(row)

# 关键是加了 encoding='utf-8-sig',这个操作是以utf-8的编码方式解读文件
# 如果没有后面的 -sig 导致程序运行后出现\ufeff
# 是因为文件中存在一个不可见的字节顺序标记(BOM),它表示文件是以UTF-8编码格式存储的
# 在读取CSV文件时,这个BOM可能会导致输出结果中出现\ufeff字符
# 为了避免这个问题,可以在打开文件时添加`utf-8-sig`编码参数,这样Python会自动处理BOM


# ['姓名', '性别', '年龄']
# ['李明', '男', '19']
# ['杨柳', '女', '18']
# ['张一凡', '男', '18']
# ['许可', '女', '20']
# ['王小小', '女', '19']
# ['陈心', '女', '19']


# 上述代码中,用 with 语句以读模式打开 stu.csv 文件后
# 针对该文件创建了一个 CSV的reader对象 reader,然后通过 for 循环遍历了对象 reader
# 最后按行输出了文件中的数据
# 从输出形式来看,每一行都以列表的形式输出,且文件中所有的数据都是字符串
# writer 对象



# 用csv模块将数据写入CSV文件时就需要创建 writer对象
# 因为 CSV 文件都是 按行 存储的,所以写文件时需要调用 writer 对象的 writerow()方法
# 将用列表存储的一行数据写入文件
# 下面,依然以 stu.csv 文件为例,通过 csv 模块的 writer 对象向文件中 追加 两条新的记录:
# " '张芳','女',20 "和" '王虎','男',18 "

import csv
with open("stu.csv",'a',encoding='utf-8') as stucsv:
    writer = csv.writer(stucsv)
    writer.writerow(['张芳','女',20])
    writer.writerow(['王虎','男',18])

# 这样写会有空行出现,对于 CSV文件,如果在记录之间出现了空行,在读文件的时候会出现错误
# 因此需修改代码段,在打开文件时增加一个参数“ newline='' ”,指明在写入新的记录后不插入空行


# 这里我手动进入了csv文件将刚刚写入的删除了
# 然后重新在敲一遍,这次加上 newline=''


import csv
with open("stu.csv",'a',encoding='utf-8',newline='') as stucsv:
    writer = csv.writer(stucsv)
    writer.writerow(['张芳','女',20])
    writer.writerow(['王虎','男',18])

# import csv
# with open("stu.csv",'a',encoding='utf-8',newline='') as stucsv:
#     writer = csv.writer(stucsv)
#     writer.writerow(['张芳','女',20])
#     writer.writerow(['王虎','男',18])


# csv 的 writer 对象也提供一次写入多行的方法 writerows()
# writerows()方法将参数列表中的每一个元素列表作为一行写入 CSV 文件

# 下列代码示范了如何调用 writerows()方法一次写入两行记录

import csv
with open("stu.csv",'a',encoding='utf-8',newline='') as stucsv:
    writer = csv.writer(stucsv)
    writer.writerows([['张三','男',20],['李四','男',18]])
# writer 对象的 writerows()方法只接受一个序列作为参数,可以是列表,也可以是元组

# 异常和异常处理


# 什么是异常

# 在学习 Python 语言的过程中,碰到过程序出错的情况,
# 有可能是引用了未定义的变量,有可能是访问了字典中不存在的键,也有可能是用读模式打开了一个不存在的文件
# 不管出现什么样的错误,都会导致程序终止运行,并输出错误信息
# 这些错误影响了程序的正常执行,被称为异常

# 在 Python 语言中,不同的异常被定义为不同的对象,对应不同的错误


# Python 语言中几种常见的异常及其描述

# Exception
# 常规异常的基类

# ZeroDivisionError
# 除数为零

# IOError
# 输入/输出操作失败

# IndexError
# 序列中没有此索引(index)

# KeyError
# 映射中没有这个键

# NameError
# 未声明/初始化对象 (没有属性)


# 异常会立刻终止程序的执行,无法实现原定的功能
# 但是,如果在异常发生时能及时捕获并做出处理,就能控制异常、纠正错误、保证程序的顺利执行



# 异常处理


# Python 语言中专门提供了 try 子句来进行异常的捕获与处理
# 最简单的 try 子句格式如下:

# try:
#   语句
# except 异常名称:
#   捕获异常时处理
# else:
#   未发生异常时处理


# 程序执行时,如果 try 子句中发生了指定的异常,则执行 except 子句部分进行异常的处理
# 如果 try 子句执行未发生异常则执行 else 子句部分


# 例7-1 从键盘输入a和 b,求a 除以的结果并输出

# 从键盘输入除数,就有可能会输入 0,而除数为 0是个很严重的错误
# 应该进行 ZeroDivisionError 异常的捕获和处理

try:
    a = eval(input("a = "))
    b = eval(input("b = "))
    c = a / b
except ZeroDivisionError:
    print('除数不能为0!')
else:
    print("c = ",c)


# a = 10
# b = 2
# c =  5.0


# a = 4
# b = 0
# 除数不能为0!

# 例7-2
# 读取并输出 E:\Python\Ptest 目录下 data2.txt 文件中的内容,如果文件不存在则提醒用户先创建文件

# 题目要求读出文件内容,需要用读模式打开
# 但是如果文件不存在系统会报错,所以需要进行IOError 异常的捕获和处理

try:
    file = open('data2.txt','r')
except IOError:
    print('文件不存在,请先创建!')
else:
    text = file.read()
    print('data2.txt文件的内容是:',text)
    file.close()

# 文件不存在,请先创建!

# 所以我们先创建文件并在里面写入内容

# 创建
file = open('data2.txt','w')

# 写入内容
file.write('我是文件data2.txt')
# 关闭文件
file.close()

# 再次执行
# try:
#     file = open('data2.txt','r')
# except IOError:
#     print('文件不存在,请先创建!')
# else:
#     text = file.read()
#     print('data2.txt文件的内容是:',text)
#     file.close()


# data2.txt文件的内容是: 我是文件data2.txt

# 根据提示信息创建了 data2.txt 文件并输入内容后
# 再次执行程序,没有触发任何异常,程序执行了 else 部分,输出了文件中保存的文本信息


# 异常处理不能“消灭”异常本身,但是却可以让原本不可控的异常及时被发现,并按照设计好的方式被处理
# 异常处理让程序不会被意外地终止,而是按照设计以不同的方式结束运行
# 在这个设计中,except后的异常类型至关重要,需要根据 try 子句的具体操作进行恰当的选择
# 如果实在不确定异常的类型,可以用通用的异常对象 Exception 来捕获


# 删除文件

# import os
# os.remove(文件路径)

# 或者
# import os
# file文件对象 = 要删除的文件路径
# os.remove(file文件对象)

# 例如:
# os.remove('E:\\Python\\Ptest\\mydata.txt')
  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值