Python学习16--文件和上下文管理器

操作系统的文件分为:
(1)文本文件:.txt
(2)二进制文件:.mp3、.wma、.doc

一、获取文件对象

运用open函数来获得 文件对象
格式为:open(file,mode)
file: 泛指文件和文件夹,
相对路径:从当前路径开始的路径
绝对路径:从盘符开始的路径如:“E:\test”
mode模式说明:
r:(1)读模式(默认模式),用来读取文件内容。文件指针在文件的开头。文件需要事先存在,否则会产生异常。
w:(1)写模式,用来向文件写入数据。文件指针在文件的开头。如果文件存在,则覆盖文件,否则会创建文件。
a:(1) 追加模式,用来向文件追加数据。文件指针在文件的末尾。如果文件存在,不会覆盖文件(追加写入),否则会创建文件。
x:(1) 写模式,用来向文件写入数据。文件指针在文件的开头。如果文件不存在,创建文件,否则产生错误。
t:(2) 文本(字符串)模式(默认模式),以文本形式操作文件。
b:(2) 二进制(字节)模式,以二进制形式操作文件。
U:(3) 通用换行符模式(已不建议使用)。在该模式下,\n,\r\n或\r都会解析为换行符。不能与w、a、x或+模式同时使用(仅限于读取文件时使用)。
+:(3) 读取或写入。
(1)(2)(3)模式中,不同组的模式可同时使用、(1)默认值是r、(2)默认值是t

f = open(r"E:\test\test1.txt","rt")
print(f)

二、关闭文件

关闭文件的几种方法:
(1)直接关闭
(2)try finally中
(3)with打开和关闭 (推荐)

使用with打开文件 with open(…) as 文件对象名1, open(…) as 文件名2

with open(r"E:\test\test1.txt","r") as f1, open(r"E:\test\test2.txt") as f2:
    print(f1.read())
    print(f2.read())

输出:

hello python2hello python2
hello python2hello python2

三、文件的读取

(1) read(size): size不写或者-1代表将文件内容全部读出,size>0整数 读取对应的字节

with open(r"E:\test\test1.txt","r") as f:
    print(f.read())

输出:

hello python
浣犲ソpython

如果要正确的显示出中文字符,需要加上encoding="utf-8"参数
如下所示:

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.read())

输出:

hello python
你好python

文件对象是迭代器,当读取到文件的末尾,无法再读取任何内容,返回空
如下文本信息为abc

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.read(1))
    print(f.read(1))
    print(f.read(1))
    print(f.read(1))

输出:

a
b
c

**(2)readline()**返回文件的一行,保留原文档中\n,将\n也认为是读取的内容
如下文本信息为:
hello1
hello2
hello3
代码执行情况如下:

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.readline())
    print(f.readline())
    print(f.readline())
    print(f.readline())

输出:

hello1

hello2

hello3

(3)readlines() 返回一个列表,每一个元素都是文件中的一行

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.readlines())

输出:

['hello1\n', 'hello2\n', 'hello3\n']

(4)通常如果文件过大,我们使用for循环的方式对文件对象进行迭代,文件会按行输出
文本信息为:
hello1 hello2
hello2
hello3
代码执行情况为:

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    for i in f:
        print(i,end="")

输出:

hello1    hello2
hello2
hello3

四、文件的写入

(1)write(content)方法

with open(r"E:\test\test1.txt","w+",encoding="utf-8") as f:
    f.write("hello2")

(2)writelines(lines) :将lines列表中的内容写入文件

with open(r"E:\test\test1.txt","w+",encoding="utf-8") as f:
    f.writelines(["hello1\n","hello2\n"])

五、文件的定位

文件的指针:
当r或者w模式的时候,指针的位置是在文件的开头0(空文件也在0的位置)
当a模式的,指针的位置在文件末尾最后一个字符的下一个位置。

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.tell())
with open(r"E:\test\test2.txt","w",encoding="utf-8") as f:
    print(f.tell())
with open(r"E:\test\test3.txt","a",encoding="utf-8") as f:
    print(f.tell())

输出:

0
0
6

(1)tell: 返回指针的位置,即下一个要读取或者要写入字节的位置

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.tell())
    f.read(1)
    print(f.tell())

输出:

with open(r"E:\test\test1.txt","r",encoding="utf-8") as f:
    print(f.tell())
    f.read(1)
    print(f.tell())

**(2)seek(offset,whence)😗*改变指针位置
offset:指定偏移量
whence:指定偏移量参照位置:
0 SEEK_SET-----从文件头开始计算
1 SEEK_CUR-----从文件当前的位置
2 SEEK_END-----从文件尾开始计算

对于whence=0的时候,可以指定b模式或者t模式
对于whence=1或2时候,只支持b模式,当t模式的是,偏移量只能是0才是有效的
如下代码文本信息为012345

with open(r"E:\test\test1.txt","br") as f:
    print(f.read(1))
    print(f.tell())
    f.seek(3,0)
    print(f.tell())
    print(f.read(1))
    f.seek(0,0)
    print(f.tell())
    print(f.read(1))
    f.seek(1,1)
    print(f.read(1))
    f.seek(-1,2)
    print(f.read())

输出:

b'0'
1
3
b'3'
0
b'0'
b'2'
b'5'

t模式下对于whence=1或2时候,只支持b模式,当t模式的是,偏移量只能是0才是有效的

with open(r"E:\test\test1.txt","tr") as f:
    print(f.read(1))
    print(f.tell())
    f.seek(3,0)
    print(f.tell())
    print(f.read(1))
    f.seek(0,0)
    print(f.tell())
    print(f.read(1))
    f.seek(0,1)
    print(f.read(1))
    f.seek(0,2)
    print(f.read())
    print(f.tell())

输出:

0
1
3
3
0
0
1

6

六、文件与路径的操作

1 .os模块

(1) mkdir: 父目录必须存在,被创建的目录必须不存在,否则报错

import os
os.mkdir(r"E:\test\mkdir_test")
import os
os.mkdir(r"E:\test1\mkdir_test")

输出:

FileNotFoundError: [WinError 3] 系统找不到指定的路径。: 'E:\\test1\\mkdir_test'

(2)makedirs: 父目录可以不存在,会同时创建父目录

import os
os.makedirs(r"E:\test1\mkdir_test")

(3)rmdir:删除空目录

os.rmdir(r"E:\test1\mkdir_test")

(4)删除空目录,删完还会查看父目录是否也为空,如果也为空,则删除父目录,直到文件夹不为空为止

import os
os.makedirs(r"E:\test1\mkdir_test")
os.removedirs(r"E:\test1\mkdir_test")

(5)删除文件,如果文件不存在,也不会报错

import os
os.remove(r"E:\test\test1.txt")

(6)rename 重命名文件或者目录

import os
os.rename(r"E:\test\test2.txt",r"E:\test\test22.txt")

(7)renames (剪切粘贴)重命名文件,可以应用不同的路径下(不能运用在不同磁盘中):
相当于把原来路径下的内容删掉,在新路径下新创建一个文件

import os
os.renames(r"E:\test\test22.txt",r"E:\test1\test22.txt")
os.remove(r"E:\test1\test22.txt")
os.rmdir(r"E:\test1")

**(8) getcwd()**返回当前工作目录

import os
cur_path = os.getcwd()
print(cur_path)

(9) walk 遍历路径下的文件
会显示每一层文件的情况
(1)dirpath: string 代表目录的路径,起始路径
(2)dirnames :list 包含dirpath下的所有子目录名字
(3)filenames:list 包含非目录文件的文件名
实际储存结构如下:
在这里插入图片描述

t = os.walk(r"E:\test")
for roots,dirs,files in t:
    print(roots,end="\t[roots]\n")
    print(dirs,end="\t[dirs]\n")
    print(files,end="\t[files]\n")

输出:

E:\test	[roots]
['test1']	[dirs]
['test3.txt']	[files]
E:\test\test1	[roots]
[]	[dirs]
['test1_next.txt']	[files]

(10)显示参数路径下的第一层目录名字,包括文件个路径(文件夹)

import os
t = os.listdir(r"E:\test")
print(t)

输出:

['test1', 'test3.txt']

2.os.path

**(1)abspath(path):**返回当前文件所在绝对路径

print(os.path.abspath("."))

(2)basepath返回路径中最后面部分,以/分隔符为准(只是字符串的处理,忽略路径是否真实存在)

import os.path
print(os.path.basename(r"E:\test\test1"))

输出:

test1

**(3)commonpath:**返回列表中所有路径的最长公共路径

import os.path
print(os.path.commonpath([r"E:\test\test1",r"E:\test\test2"]))

输出:

E:\test

(4)exist 判断路径是否存在

import os.path
print(os.path.exists(r"E:\test\test1"))

输出:

True

(5)返回目录或者文件的最后访问时间,修改时间

import os.path
print(os.path.getatime(r"E:\test\test3.txt"))
print(os.path.getctime(r"E:\test\test3.txt"))

输出:

1596352967.0744138
1596352552.2928417

(6)getsize获得文件的大小 字节为单位

import os.path
print(os.path.getsize(r"E:\test\test3.txt"))

输出:

6

(7)判断是否存在目录或者文件

import os.path
print(os.path.isdir(r"E:\test\test3.txt"))

输出:

False

(8)join路径的拼接
绝对路径前的元素都去掉
空元素会去掉

print(os.path.join("abc", "c:/", "def", "", "ghi"))

输出:

c:/def\ghi

(9)拆路径: dirname和basename

import os.path
print(os.path.split("c:/abc/def/ghi.jpg"))

输出:

('c:/abc/def', 'ghi.jpg')

3. shutil模块

(1)复制:复制内容
如果目标文件夹不存在,不会报错也不会执行

import shutil
shutil.copy(r"E:\test\test3.txt",r"E:\test\test1")

(2)copy2 把元信息都复制,包含文件的创建时间等等信息

import shutil
shutil.copy2(r"E:\test\test3.txt",r"E:\test\test1")

(3)copytree 复制目录,连同子目录和文件都复制
目标文件不能存在,否则报错:

FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: 'E:\\test\\test2'
shutil.copytree(r"E:\test",r"E:\test\test2")

七、序列化

即把list转换成str,有如下优点:1、传输快;2、解析list的任务给了客户端
序列化的形式有如下三种:
1、csv;
2、json;
3、pickle;

1、csv

向csv格式的文件中写数据,一次写一行:writerow

import csv
with open("c:/abc/test.csv","wt") as f:
    writer=csv.writer(f)
    writer.writerow(["姓名","年龄","小组"])
    writer.writerow(["张三","15","A组"])
    writer.writerow(["李四","16","B组"])

向csv格式的文件中写数据,一次写多行:writerows
newline表示写操作的时候,调用一次witer会加\n

import csv
with open(r"D:\test/test.csv","wt") as f:
    writer=csv.writer(f)
    writer.writerows([["姓名","年龄","小组"],["张三","15","A组"],["李四","16","B组"]])

2、json:javascript object notation

轻量级的数据交换格式
采用键值映射,:
对象:{}
数组:[]
字符串:""
布尔类型:true false
数值:5 2.8
python的数据类型跟json几乎一致,python和json的之间转换很方便

1、从python的字典数据转换成json的文件:【dump】
第一个参数是要写入的数据,第二个参数是文件对象
ensure_ascii=默认值是True,如果希望显示中文ensure_ascii=False

d={
    "bg": "green",
    "title": {
        "data": ["data1", "data2", "data3", "data4"],
        "align": "左对齐"
    }
}
import json
with open("d:/test/test.json","wt") as f :

    json.dump(d,f,ensure_ascii=False)

2、将json格式的转换成python类型数据

d={
    "bg": "green",
    "title": {
        "data": ["data1", "data2", "data3", "data4"],
        "align": "左对齐"
    }
}
import json
with open("d:/test/test.json","wt") as f :

    json.dump(d,f,ensure_ascii=False)
with open("d:/test/test.json","rt") as f :

    d=json.load(f)
    print(type(d))
    print(d)

输出:

<class 'dict'>
{'bg': 'green', 'title': {'data': ['data1', 'data2', 'data3', 'data4'], 'align': '左对齐'}}

3序列化和反序列化
序列化:将字典类型的对象转换成字符串 dumps
第一个参数:需要序列化的字典对象,ensure_ascii=False

d={
    "bg": "green",
    "title": {
        "data": ["data1", "data2", "data3", "data4"],
        "align": "左对齐"
    }
}
import json
s=json.dumps(d,ensure_ascii=False)
print(type(s))
print(s)

输出:

<class 'str'>
{"bg": "green", "title": {"data": ["data1", "data2", "data3", "data4"], "align": "左对齐"}}

反序列化:将字符串转换成python中字典 loads;
第一个参数:需要反序列化的字符串对象

d={
    "bg": "green",
    "title": {
        "data": ["data1", "data2", "data3", "data4"],
        "align": "左对齐"
    }
}
import json
s=json.dumps(d,ensure_ascii=False)
d2=json.loads(s)
print(type(d2))
print(d2)

输出:

<class 'dict'>
{'bg': 'green', 'title': {'data': ['data1', 'data2', 'data3', 'data4'], 'align': '左对齐'}}

4、python中的字典和json类型之间映射

import json
d_new={
    "布尔类型":False,
    "空值类型":None,
    "浮点类型":1.5,
    "整数类型":1,
    "字符串":"abc",
    "列表/数组":[1,2,3],
    "字典/对象":{"one":1,"two":2}
}
print(json.dumps(d_new,ensure_ascii=False))

输出:

{"布尔类型": false, "空值类型": null, "浮点类型": 1.5, "整数类型": 1, "字符串": "abc", "列表/数组": [1, 2, 3], "字典/对象": {"one": 1, "two": 2}}

5、自定义序列化类型,继承JSONEncoder,重写default方法

import json
class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
p=Person("张三",20)
class PersonEncoder(json.JSONEncoder):
    def default(self, o):
        # 变成字典
        if isinstance(o,Person):
            return {"name":o.name,"age":o.age,"自定内容":"自定义的值"}
        else:
            return super().default(o)

# 调用自定义的序列化
json_str=json.dumps(p,cls=PersonEncoder,ensure_ascii=False)
print(json_str)
print(type(json_str))

3、pickle

第一个参数是需要序列化的对象
第二个参数是序列化之后存储的文件

import pickle
class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
p=Person("张三",20)
with open("d:/test/person.pickle","wb") as  f:
   
    pickle.dump(p,f)

with open("d:/test/person.pickle","rb") as f:

    p2=pickle.load(f)
print(type(p2))
print(p2.name)
print(p2.age)

输出:

<class '__main__.Person'>
张三
20

八、上下文管理器

with语句体实现的是上下文管理器,在with开始执行和退出的时候,会自动调用一些魔法方法。
开始执行时自动调用__enter__方法,退出时自动调用__exit__方法。可以通过重写这两个方法来重写上下文管理器。
当进入with语句体时候,会默认自动调用__enter__,返回值是with语句体绑定的对象
当退出with语句体的时候,会默认调用__exit__,当with语句体没有异常时,参数全部是None
exc_type:with中产生的异常类型
exc_val:异常对象
exc_tb:异常堆栈轨迹回溯对象
__exit__方法的返回值None(正常抛出异常,程序中断,等待捕捉异常)
返回值True 镇压异常


```python
class MyManger:
    def __enter__(self):
        print("进入with语句体")
        return "__enter__方法的返回值"
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        print("离开with语句体")


with MyManger() as m:
    print("执行with语句")
    print(m)

输出结果为:

进入with语句体
执行with语句
__enter__方法的返回值
None
None
None
离开with语句体

当程序正常执行时, exc_type, exc_val, exc_tb返回值均为None
当程序执行有异常时,执行结果如下:

class MyManger:
    def __enter__(self):
        print("进入with语句体")
        return "__enter__方法的返回值"
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exc_type是{}".format(exc_type))
        print("=============================")
        print("exc_val是{}".format(exc_val))
        print("==============================")
        print("exc_tb是{}".format(exc_tb))
        print("================================")
        print("离开with语句体")


with MyManger() as m:
    print("执行with语句")
    print(m)
    raise TypeError("手动抛出的异常")
    

输出结果为:

Traceback (most recent call last):
进入with语句体
  File "E:/new.py", line 18, in <module>
    raise TypeError("手动抛出的异常")
执行with语句
__enter__方法的返回值
exc_type是<class 'TypeError'>
TypeError: 手动抛出的异常
=============================
exc_val是手动抛出的异常
==============================
exc_tb是<traceback object at 0x000001DBAAE87208>
================================
离开with语句体

在__exit__函数中return Ture可以镇压异常,函数不会报错,正常执行。如下所示:

class MyManger:
    def __enter__(self):
        print("进入with语句体")
        return "__enter__方法的返回值"
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exc_type是{}".format(exc_type))
        print("=============================")
        print("exc_val是{}".format(exc_val))
        print("==============================")
        print("exc_tb是{}".format(exc_tb))
        print("================================")
        print("离开with语句体")
        return True


with MyManger() as m:
    print("执行with语句")
    print(m)
    raise TypeError("手动抛出的异常")

输出:

进入with语句体
执行with语句
__enter__方法的返回值
exc_type是<class 'TypeError'>
=============================
exc_val是手动抛出的异常
==============================
exc_tb是<traceback object at 0x000001E4ABEF7248>
================================
离开with语句体

通过装饰器实现上下文管理器
使用yield作为分界点,
enter方法—yield之前的语句
exit方法----yield之后的内容
enter方法的返回值—yield的产生值—with语句体的绑定对象

from contextlib import contextmanager
@contextmanager
def gen():
    print("进入with语句体执行enter")
    try:
        yield "产生一个值"

    except:  #跟exit返回Ture是一样的,镇压异常
        pass

    finally:
        print("with离开时执行exit")

with gen() as f:
    print(f)
    raise TypeError("手动抛出异常")

输出:

进入with语句体执行enter
产生一个值
with离开时执行exit

如果没有except时,异常会报出来,如下:

from contextlib import contextmanager
@contextmanager
def gen():
    print("进入with语句体执行enter")
    try:
        yield "产生一个值"
    finally:
        print("with离开时执行exit")

with gen() as f:
    print(f)
    raise TypeError("手动抛出异常")

输出:

Traceback (most recent call last):
  File "E:/new.py", line 15, in <module>
    raise TypeError("手动抛出异常")
TypeError: 手动抛出异常
进入with语句体执行enter
产生一个值
with离开时执行exit
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值