Python的文件操作

  • 文件的打开与关闭
  • 文件的读取与写入

文件的打开

  • 内置函数:open(文件名, 模式)
  • os模块的函数:os.open(文件名, 模式)
  • with open(文件名, 模式) as file

文件的关闭

  • fileobject.close() fileobject是文件对象
  • os.close(file) 关闭文件
1
2
3
4
5
file_1 = open('F:\\file_1.txt', 'r')
print('file_1---', file_1, type(file_1))

#关闭文件之后,python才能释放对该文件的控制,可以去尝试删除该文件
file_1.close()

默认的文件编码encoding一般依赖于系统的区域设置和Python的版本。 cp936代表GBK编码 标准编码参考:https://docs.python.org/3/library/codecs.html

1
2
3
import io    #io.open()是内置函数open的别名,本质是一样的
file = io.open('F:\\file_1.txt', 'r')
print('file---', file, type(file))
1
2
3
4
5
import os
file_2 = os.open('file_2.txt', os.O_RDWR|os.O_CREAT) #os.O_RDONLY
print('file_2--', file_2, type(file_2))
os.close(file_2)
# os.remove('file_2.txt')

区别:

  • open()函数返回的是一个文件对象,而os.open()函数返回的是一个文件描述符(即整数)。
  • python内置的open()函数主要用于处理文本文件,而os.open()函数主要用于处理二进制文件和低级文件操作。

使用with语句即python的上下文管理器,它可以自动关闭文件,而用open()函数则需要手动关闭文件。

1
2
3
with open('F:\\file_1.txt', 'rb+') as f:
content = f.read()
print(content)

文件的打开模式

  • ‘r’:只读模式
  • ‘w’:覆盖写模式,不存在则创建,存在则完全覆盖
  • ‘a’:追加写模式,不存在则创建,存在则在文件最后追加内容
  • ‘x’:排他性创建模式:如果文件不存在,创建并打开写模式的文件。如果文件已存在,操作将失败。
  • ‘b’:二进制文件模式
  • ‘+’:在原功能基础上增加同时读写功能,可与r/w/a组合。

思考下列代码分别属于什么打开模式:

  1. open(‘file.txt’, ‘w’) 写入模式
  2. open(‘file.txt’, ‘w+’) 读写模式
  3. open(‘file.txt’, ‘rb’) 二进制读取模式
  4. open(‘file.txt’, ‘rb+’) 二进制读写模式

文件的读取与写入

  • read()读取文件内容
  • readline()返回文件的一行内容,格式为字符串
  • readlines()返回文件所有行内容,格式为列表
  • write()写入文件内容
  • writelines()将一个字符串列表写入文件
1
2
3
4
#read()读取文件内容
with open('F:\\file_1.txt', 'r') as f:
content = f.read()
print(content)
1
2
3
4
5
6
7
8
9
#readline()读取一行内容
f = open('F:\\file_1.txt', 'r')
print(f.readline(5))

# while True:
# line = f.readline() #f.readline(4) 读取前几个字符
# if not line:
# break
# print(line, type(line))
1
2
3
4
#readlines()逐行读整个文件内容,包括换行符
with open('F:\\file_1.txt', 'r') as f:
fa = f.readlines()
print(fa, type(fa))
1
2
3
# w覆盖写入,write()参数必须为字符串,如果想换行,必须加上换行符\n
with open('F:\\file_1.txt', 'w') as file:
file.write('Hello, World!')
1
2
3
4
# 写入字符串列表,如果想换行,必须加上换行符\n
with open('F:\\file_1.txt', 'w') as file:
lines = ['Hello,\n', 'World2!','black Monkey'] #\n
file.writelines(lines)

绝对路径和相对路径

  • 绝对路径是从根目录开始的完整路径。它提供了到达指定文件或目录的具体位置,不依赖于当前工作目录的位置。
    系统中的绝对路径E:\jupyter\python,python中的绝对路径r"E:\jupyter\python"或者E:/jupyter/python或者E:\\jupyter\\open
  • 相对路径是相对于当前工作目录的路径。它通过相对位置来指定文件或目录,简化了路径的表达,但其具体位置依赖于当前的工作目录。

os模块常用的路径操作

  1. 获取当前路径名

    1
    2
    3
    4
    #1.获取当前路径名
    import os
    print(os.getcwd())
    print(os.path.abspath(os.curdir)) #返回绝对路径 os.path.abspath
  2. 获取父目录路径名

    1
    2
    print(os.path.dirname('../python/6.1 Python的文件操作.ipynb'))
    print(os.path.dirname(os.path.join('../', 'open', 'op', 'file.txt')))
  3. 返回指定路径下的文件和目录列表

    1
    2
    3
    #3. 返回指定路径下的文件和目录列表
    #'.'或'./'代表当前路径 #'..'或'../'代表上一级路径
    print(os.listdir('..'))
  4. 创建目录

    1
    2
    #4.在当前文件夹下创建单层目录
    os.mkdir('new1123')
  5. 合并两个或多个路径名组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import os
    #5. 合并两个或多个路径名组件
    print(os.path.join('../', 'open'))

    #合并一个没有的目录,需要重新创建目录
    # os.mkdir(os.path.join('../', 'open'))

    print(os.path.join('../', 'open','./', 'file.txt'))

    with open('new1123', 'w') as fs:
    fs.write('111222333')
  6. 统一规范化路径分隔符

    1
    2
    3
    4
    5
    6
    #6.统一规范化路径分隔符
    file_op = os.path.join('../', 'open','./' , 'file.txt')
    uni_op = file_op.replace(os.sep, '/') #直接对字符串进行替换
    normalized_path = os.path.normpath(file_op) #用方法规范化
    print(uni_op)
    print(normalized_path)
  7. 判断文件是否存在

    1
    2
    3
    4
    5
    #7.判断文件是否存在
    if os.path.exists('F:\\file_2.txt'):
    print('yes')
    else:
    print('no')

更多的os操作请查看官方文档:https://docs.python.org/3/library/os.html#module-os

csv格式文件的读取和写入

  • csv是逗号分隔符文本格式,常用于Excel和数据库的数据导入和导出。

  • csv的读取:csv.reader(csvfile, dialect='excel', **fmtparams)#csvfile文件对象;dialect指定csv格式;fmtparams用于指定特定格式覆盖dialect的格式

  • csv.reader()创建的是可迭代对象

1
2
3
4
5
6
7
8
#csv的读取
import csv

with open('F:\\stu.csv') as f:
f_csv = csv.reader(f)

for row in f_csv:
print(row)
  • csv的写入:csv.writer(csvfile, dialect='excel', **fmtparams)
  • csv文件.writerow(row) #写入一行
  • csv文件.writerows(rows) #写入多行
1
2
3
4
5
6
7
header = ['id', 'name', 'sex', 'work']
datas = [['8090', 'juney', 'male', 'teacher'],['8091', 'marry', 'female', 'nurse']]

with open('F:\\stu.csv', 'w') as f:
w_csv = csv.writer(f)
w_csv.writerow(header)
w_csv.writerows(datas)

在Windows上的换行符为\r\n,而在Linux和MacOS上的换行符为\n

open()中有个参数’newline’控制如何处理换行符。 可令newline=''

此参数在不同操作系统间提供一致的换行符处理,避免了跨平台文本文件处理中的问题。

newline参数只对文本模式有效,对二进制模式无效。

1
2
3
4
5
6
7
header = ['id', 'name', 'sex', 'work']
datas = [['8090', 'juney', 'male', 'teacher'],['8091', 'marry', 'female', 'nurse']]

with open('F:\\stocks1.csv', 'w', newline='') as f:
w_csv = csv.writer(f)
w_csv.writerow(header)
w_csv.writerows(datas)

读取’green_red’文件夹下的’train’文件夹,然后读取里面的csv文件,把里面的前10行数据放到新的列表中。

1
2
3
4
5
6
7
8
9
10
11
import csv
with open('green_red/train/Glass_train.csv', 'r+') as fcsv:
fs = csv.reader(fcsv)
li_fs = list(fs)
li_csv = []
for i, v in enumerate(li_fs):
li_csv.append(li_fs[i])
if i == 9:
break

print(li_csv)

对象序列化和pickle模块

  • 序列化是Python对象转换为数据格式,以便在不同的环境中存储、传输和重建。
  • 反序列化是从磁盘文件或所接收到的数据形式恢复,得到相应对象的过程。
  • 对象序列化广泛用于各种分布式并行处理系统。
  • pickle模块可以实现对象的序列化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pickle

#序列化
data = {'key': 'value', 'num': 123}

# 对象转换为字节流,'b'代表二进制模式
with open('data.pickle', 'wb') as file:
# 使用pickle的dump函数将数据序列化并保存到文件
pickle.dump(data, file)


#反序列化
with open('data.pickle', 'rb') as file:
# 使用pickle的load函数从文件加载数据
data_loaded = pickle.load(file)

print(data_loaded)

应用实例:

  • 将一个整数列表[1, 2, 3, 4, 5]序列化到一个名为numbers.pkl的文件中。
  • 从numbers.pkl文件中反序列化该列表,并将其打印出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pickle

# 要序列化的整数列表
numbers_list = [1, 2, 3, 4, 5]

# 序列化整数列表到文件 numbers.pkl 中
with open('numbers.pkl', 'wb') as file:
# 使用 pickle.dump 函数将列表序列化并保存到文件
pickle.dump(numbers_list, file)
print("列表已序列化到文件 numbers.pkl 中。")

# 从文件 numbers.pkl 中反序列化列表
with open('numbers.pkl', 'rb') as file:
# 使用 pickle.load 函数从文件加载并反序列化列表
loaded_numbers_list = pickle.load(file)
print("列表已从文件 numbers.pkl 中反序列化。")
print("反序列化后的列表内容:", loaded_numbers_list)

文件的筛选:

  • file.endswith() #以…结尾的文件名
  • file.startswith() #以…开始的文件名
1
2
3
4
5
# 假设我们有一个文件名列表
filenames = ['example.txt', 'data.csv', 'image.png', 'notes.txt']
# 筛选出所有以 '.txt' 结尾的文件
text_files = [file for file in filenames if file.endswith('.txt')]
print(text_files)
1
2
3
4
5
# 假设我们有一个文件名列表
filenames = ['report2024.txt', 'summary2024.csv', 'image2024.png', 'notes2024.txt']
# 筛选出所有以 'report' 开始的文件
report_files = [file for file in filenames if file.startswith('report')]
print(report_files)

Python的异常捕获

错误与异常

错误

  • 错误(Error)可以分为语法错误和逻辑错误,当运行时出现报错信息,此时就产生了异常(Exception)。

    1
    2
    3
    4
    5
    #语法错误在编译阶段就会报错
    def op(a):
    a = 1

    list([1,2,3)

异常

  • 程序运行时检测到的错误称为异常,异常不一定导致严重的后果,而是显示错误信息。

Python中常见的异常

更多的内置异常类请参考官方文档:https://docs.python.org/3/library/exceptions.html#index-2

  1. NameError:尝试访问一个未声明的变量
  2. ZeroDivisionError:除数为0
  3. SyntaxError:解释器语法错误
  4. IndexError:请求的索引超出序列范围
  5. TypeError:类型错误
  6. KeyError:请求一个不存在的字典键
  7. FileNotFoundError:请求不存在的文件或目录时
  8. FileExistsError:创建一个已经存在的文件或目录时

异常处理

  1. try-except语句
1
2
3
4
try:
try_suite # 检测是否有异常
except exceptiontype:
except_suite # 异常处理代码
  • 首先,执行 try 子句,如果没有触发异常,则跳过 except 子句,try 语句执行完毕。

  • 如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。判断异常类型与 except 指定的异常是否匹配,若匹配则会执行 except 子句。若不匹配,异常就会被递交至上一级,也就是由该段代码的调用者去处理。如果最后还是无法解决的话,就会出现错误,导致程序崩溃。

  • except可以有不同的异常类型,但最多只有一个处理程序会被执行。第一个匹配到的异常类型将被处理,其他匹配到的异常类型将不会被处理。

1
2
except (RuntimeError, TypeError, NameError):
pass
  • 如果想要处理所有匹配到的异常类型,并且在每个异常类型中执行不同的代码,可以使用多个except语句,每个语句处理一个特定的异常类型。
1
2
3
4
5
6
7
8
try:
pass
except RuntimeError:
return "RuntimeError occurred"
except TypeError:
return "TypeError occurred"
except NameError:
return "NameError occurred"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
try:
import mathp
type_e = float(25)
s = math.sqrt(type_e)
except (ImportError, TypeError):
print("发生了一个异常")

# import math
# type_e = float(2)
# s = math.sqrt(type_e)
# print(s)

#放到try-except里
try:
import mathoo
type_e = float(2)
s = math.sqrt(type_e)
except TypeError:
print('🐖🐖🐖float-TypeError')
except ImportError:
print('🐇🐇🐇import-ImportError')

print(s)

2.try-except-else语句

1
2
3
4
5
6
try:
try_suite # 检测是否有异常
except exceptiontype as name:
except_suite # 异常处理代码
else:
else_suite #没有发生异常时执行
1
2
3
4
5
6
7
8
9
10
11
def divide(x, y):
try:
y -= 1
result = x / y
except Exception as e:
return e
else:
return result

print(divide(3, 1))
print(divide(3, 3))
  1. try-finally 语句
1
2
3
4
try:
...
finally:
...
  1. try-except-else-finally语句
1
2
3
4
5
6
7
8
try:
...
except MyException:
...
else:
...
finally:
...

无论异常是否发生,无论异常是否被捕捉到,finally后的语句块一定会被执行。

1
2
3
4
5
6
7
8
#不管try返回了什么值,如果finally中也有返回值,则最终返回finally的。
def bool_return():
try:
return True
finally:
return False

bool_return()
1
2
3
4
5
6
7
8
9
10
11
12
13
#如果try语句要执行break、continue或return语句,finally子句将在break、continue或return语句执行之前执行
try:
print("这是try块中的内容")
for i in range(5):
print('i:', i)
if i == 2:
break

except Exception as e:
print("发生了一个异常", e)

finally:
print("这是finally块中的内容")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#如果try发生异常,执行完finally语句后,会重新抛出异常
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
finally:
print("executing finally clause")

# divide(2, 1)
# divide(2, 0)
divide("2", "1")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#如果finally子句执行break、continue或return语句,则不会重新引发异常
def example():
try:
a = 1 / 0
except Exception as e:
print('eeee:eeee')
return e
finally:
print("...")
return "finally"


result = example()
print(result)

抛出异常

  • 根据程序的运行状态或满足的条件,主动引发一个指定的异常,而不是等待Python解释器在执行过程中遇到错误时自动抛出异常。
  • 使用raise语句抛出异常,此异常可以是BaseException的子类、由BaseException派生的自定义类或异常类的实例。
  • 直接使用异常类raise ValueError;派生Exception类的自定义异常类class MyError(Exception): pass; raise MyError;使用异常类的实例raise ValueError('输入值有误')
1
2
3
4
5
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise

异常链

  • Python中的异常链是指显式地将异常链接在一起,将一个异常链接到另一个异常的能力。当一个异常作为另一个异常的直接后果而发生时,这个特性特别有用。通过链接异常,开发人员可以提供更多的上下文和更清晰的回溯,从而更容易调试。
  • 用from语句链接异常,并与raise语句合用形成基本结构:raise NewException from exception
  • 在某些情况下,不想关注其他异常,可以使用from None

捕获所有类型的异常

  • BaseException是所有异常的公共基类。
  • BaseException的一个子类 Exception 是所有非致命异常的基类,所有内置的、非系统退出的异常都派生自这个类。所有用户定义的异常也从这个类派生。
  • SystemExitKeyboardInterrupt是用户希望中断程序时的异常;GeneratorExit是生成器或协议中断时的异常。
  • 处理异常最常见的模式是 打印 或 记录异常。

捕获BaseException可能会意外地捕获到SystemExit和KeyboardInterrupt等异常,这可能会导致程序在应该正常退出或响应用户中断的情况下继续运行。Exception确保不阻碍正常的程序退出,也能专注于处理程序中可能发生的错误。
所以通常情况下我们使用Exception去捕获几乎所有的异常情况。
使用Exception捕获异常时,通常后面加一个异常参数e(也可以是其他的变量名),它用于打印异常的具体细节。

文件读取与异常捕获实例:

编写一个Python脚本,尝试读取一个不存在的文件。

使用try-except语句捕获并处理FileNotFoundError异常。

使用else子句打印一条成功读取文件的消息。

使用finally子句确保文件被正确关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try:
# 尝试打开一个不存在的文件
with open('non_existent_file.txt', 'r') as file:
content = file.read()
except FileNotFoundError:
# 如果文件不存在,捕获 FileNotFoundError 异常
print("文件未找到,请检查文件名是否正确。")
else:
# 如果没有异常发生,打印成功读取文件的消息
print("文件读取成功!")
print(content)
finally:
# 无论是否发生异常,都执行 finally 块中的代码
print("操作完成。")