【Python基础篇】【16.文件与深浅拷贝】(附案例,源码)文件夹及目录操作

2023-12-17 18:29:18

八、文件与深浅拷贝

文件

为了保存数据,方便修改和分享,数据通常以文件的形式存储在磁盘等外部存储介质中。根据逻辑上编码不同可将文件大致分为两类:文本文件二进制文件

文件是指为了重复使用或长期使用的目的,以文本或二进制形式存放于外部存储器(硬盘、U盘、光盘等)中的数据保存形式,文件是信息交换的重要途径,也是利用程序解决实际问题的重要媒介

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 程序对数据读取和处理都是在内存中进行的,程序设计结束或关闭后,内存中的这些数据也会随之消失。计算机文件可以将数据长期存储下来反复使用,不会因程序结束或断电而消失
  • 程序可以随时读取文件里的全部或部分数据,数据的处理结果写入文件后,可以长期保存,供其他程序的应用随时读取和处理。而且,文件的使用,还可以消除计算机内存对数据体积的限制,可以处理远超过内存大小的数据量

打开文件

# open() 函数
"""
  file参数  : 就是操作文件的位置(文件在电脑种保存的路径)
            绝对路径 :  从盘符的根目录开始指定的文件路径  D:\qd\qd1
            相对路径:   相对于当前编辑的py文件来说
  mode: 文件操作的模式 指定你需要用什么方式操作这个文件
        w  write 写入模式
        a  append 写入,追加写入
        r  read 读取
        b  binary  二进制数据操作模式<图片/视频/音频> wb  rb
 encoding :文件的编码
       window : gbk  utf-8
       mac :  uft-8
"""
# 1. 打开文件
# w 没有就会被创建
# 通过绝对路径创建文件
f = open(file='D:\\qd\\qd1\\kk.txt',mode='w') #为了防止路径有特殊字符报错 最好多加一个\

# 通过相对路径来创建文件
f1 = open(file='kk.txt',mode='w')

# 2. 进行操作
# w 写入
f1.write('666')
f1.write(111)  #是数值类型不是字符串,所以报错
# 不同模式只支持单一操作
print(f1.read())

# open函数的文件操作只能写入两种类型的数据: str / 二进制数据
# 3. 关闭 (数据有可能会泄露)
f1.close()


''' 操作模式 '''
# mode: 文件操作的模式指定你需要用什么方式操作这个文件
        # w  write 写入模式
        # a  append 写入,追加写入
        # r  read 读取
        # b  binary  二进制数据操作模式<图片/视频/音频> wb  rb

""" w模式 """
f = open(file='k1.txt', mode='w')  # w 写入模式如果文件不存在则自动创建
print(f)  # 返回的是一个对象
f.write('777')
f.write('999')  # 每一次的w写入会把原有的数据覆盖
print(f.read())  # 如果是w 那么当前这个对象只支持写入,不支持读,只能进行写入操作
f.close()

# w+ 
f = open(file='k5.txt', mode='w+', encoding='utf-8')  # 文件不存在会被创建
f.write('666')  # 默认光标会在最后
f.seek(0)  # 需要先把光标移动到最前面在读
print(f.read())

""" a模式 """  # 追加写入
# a  append 写入,追加写入
f = open(file='k2.txt', mode='a')  # 如果文件不存在就会创建
f.write('776')  
f.write('999')  # 追加写入,不会被覆盖
# 关闭文件
f.close()

# a+
f = open(file='k6.txt', mode='a+', encoding='utf-8')  # 文件不存在会被创建
f.write('888')  # 光标默认到最后  追加写入
f.seek(0)  # 需要先把光标移动到最前面在读
print(f.read())

""" r模式 """
f = open(file='k2.txt', mode='r')  # 如果文件不存在则报错
print(f.read())  # 读取文件的内容
f.write('666')  # 每个模式都会限制自己的功能
# 关闭文件
f.close()

# r+ 
f = open(file='k5.txt', mode='r+', encoding='utf-8')  # 文件不存在会报错
print(f.read())
f.write('999')  # 覆盖写入(是和光标位置有关系的)
f.seek(0)  # 需要先把光标移动到最前面在读
print(f.read())


'''
总结:
w 和 a 模式 - 如果文件不存在则创建该文件;如果文件存在,`w` 模式先清空再写入,`a`模式直接末尾追加
r 模式 - 如果文件不存在则报错
'''

打开模式
模式描述
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+打开一个文件用于读写。文件指针将会放在文件的开头。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

读取文件

''' rand '''
# 表示要从文件中读取的数据的长度(单位是字节),如果没有传入参数,那么就表示读取文件中所有的数据
f = open(file='k2.txt', mode='r', encoding='utf-8')  # 读取中文要指定编码
print(f.read())  # 默认读取全部内容
print(f.read(6))  # 如果在 read 方法中指定整型数字, 那么是按照字符数量读取数据
print(f.read(12))  # 如果超出字符数量 那么会返回所有数据

f.close()

''' randlines '''
# readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素
f = open(file='k2.txt', mode='r', encoding='utf-8')
print(f.readlines()) # 会把每一行数据和换行符进行读取,并且返回一个列表

# 比如第一行是9个字符 你如果输入的字符数小于9 那么他是会默认返回整个一行的
print(f.readlines(9))  
# 如果读取到第一行的换行符那么他会默认的读取第二行,以此类推

''' readine '''
# readline() 一次读取一行内容
f = open(file='k2.txt', mode='r', encoding='utf-8')
print(f.readline())  # 默认返回第一行数据   每一次只会默认读一行
print(f.readline(15))  # 每一次只会默认读一行  参数也是读取字符数量,如果参数超过一行的字符数,那么就会把一行整个返回
print(f.readline())
print(f.readline())

while True:
    txt = f.readline()
    if txt:  # 如果有内容返回True
        print(txt)
    else:
        break
f.close()

with管理文件

# 文件操作, 打开文件操作完后都需要关闭文件, 比较麻烦。 with open 默认关闭文件

# with open 是有自己的专属代码块的
with open(file='k9.txt', mode='w', encoding='utf-8') as f:
    f.write('888')

二进制数据

import requests

response = requests.get('https://img2.baidu.com/it/u=2565082302,1715584895&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500')
img_rr = response.content  # 返回一个二进制数据
print(img_rr)
# 图片/视频/音频  都属于二进制数据
# 二进制数据不支持编码

with open('二哈.png', mode='wb') as f:
    f.write(img_rr)

文件夹及目录操作 - OS

在日常使用计算机时,经常需要列出一个文件夹或者目录的内容,创建和删除文件,以及做其他一些比较无聊但是不得不做的“家务活”。在 Python 程序中可以做到同样的事,甚至能做更多的事。这些功能是否能减少你的工作量呢?我们拭目以待。

Python 在模块 os(操作系统,operating system)中提供了许多系统函数,本章的所有程序都需要导入这个模块。

Python 的 os 模块封装了常见的文件和目录操作,本文只列出部分常用的方法,更多的方法可以查看官方文档

os 目录操作

方法说明
os.mkdir创建目录
os.rmdir删除目录
os.rename重命名
os.remove删除文件
os.getcwd获取当前工作路径
os.chdir修改当前工作目录

os.path 路径操作

方法说明
os.path.join连接目录与文件名
os.path.split分割文件名与目录
os.path.abspath获取绝对路径
os.path.dirname获取路径
os.path.basename获取文件名或文件夹名
os.path.splitext分离文件名与扩展名
os.path.isfile判断给出的路径是否是一个文件
os.path.isdir判断给出的路径是否是一个目录
os.path.exists检查文件是否存在
import os  # 模块要先导入后使用

# 创建目录 文件夹
name = 'kk'
os.mkdir(name)  # 如果文件夹存在要创建就会报错

# 删除文件夹    # 如果文件夹不存在就会报错 如果文件夹里面有文件那么会报错
os.rmdir(name)  # 只能删除一个空的文件夹

# 删除文件
os.remove('kk\\t1.txt')  # 如果文件不存在就会报错

# 重命名, 可以针对文件/文件夹
# 针对文件夹
os.rename('kk', 'ii')  # 如果文件不存在就会报错

# 针对文件
os.rename('二哈.png', '小二哈.jpg')  # 如果文件不存在就会报错

''' 获取工作目录 '''
print(os.getcwd())  # 返回的是一个绝对路径
# 需求 :我现在要在当前创建一个kk3.txt的文件,还要写入555
result = os.getcwd() + '\\' + 'kk3.txt'
# 利用w模式不存在则自己创建的性质
with open(file=result, mode='w', encoding='utf-8') as f:
    f.write('555')  # 只支持str\二进制
    
''' 修改工作目录 '''
print('当前默认的工作目录:', os.getcwd())
os.chdir('ii')  # 以相对路径的方式修改工作目录
print('当前默认的工作目录:', os.getcwd())

''' 列举所有文件和文件名 '''
# 列出目录下所有的内容(文件、文件夹) 返回的是一个列表
a = os.listdir('ii')  # 绝对路径和相对路径都是可以的
for i in a:
    try:  # 防止报错
        os.remove('ii' + '\\' + i)  # 只能用于删除文件
    except:
        os.rmdir('ii' + '\\' + i)  # 只能用于删除文件夹
        
''' 路径的操作 '''
print('当前的绝对路径: ',os.path.abspath('.')) # 当前文件
print('当前的绝对路径: ',os.path.abspath('..')) # 上一级文件
print(__file__)  # 返回的是当前文件绝对路径
print(os.path.basename(__file__))  # 返回是的当前py文件的名字
print(os.path.dirname(__file__))  # 获取绝对路径
print(os.path.splitext(__file__))  # 文件路径和尾缀分开,返回的是一个元组

''' 路径判断 '''
dirname = 'code'
# isdir 判断是否为 文件夹
print(os.path.isdir(dirname))  # 返回布尔结果

# isfile 判断是否为文件
print(os.path.isfile(dirname))  # 返回布尔结果
# os.path.exists 检查路径(文件、目录)是否存在  #返回布尔结果
print(os.path.exists('小二哈.jpg'))
print(os.path.exists('code'))
# 当文件夹不存在就创建, 存在就不管他
# 避免文件存在重复创建报错
if not os.path.exists('aaa'): # if 后面对的条件为 True 才执行下面代码
    os.mkdir('aaa')
案例 - 批量命名
# 需求1: 把code文件夹所有文件重命名 Python_xxxx
# 需求2: 删除Python_ 重命名:1, 构造条件的数据 2. 书写if
import os
# 第一步:打印当前的工作目录
print(os.getcwd())
a = 2
os.chdir('code')
# print(os.getcwd())
file_name = os.listdir()
for i in file_name:
    if a == 1:  # 执行需求1
        new_name = 'Python_' + i
    else:   # 执行需求2
        new_name = i[7:]  # 索引为7开始取值

    os.rename(i, new_name)
案例 - 复制内容
# 需求 : 将模拟文件种的文件复制到另一个文件夹去,数据也要写入进去
# 文件名字后面统一加 : [复制]
import os
print(os.getcwd())
for file in os.listdir('模拟'):
    print(file)
    # 创建一个备用文件夹,用于存放复制的文件
    if not os.path.exists('备份文件夹'):  # if 后面对的条件为 True 才执行下面代码
        os.mkdir('备份文件夹')

    # 读取旧的数据
    with open(file='模拟\\' + file, mode='r', encoding='utf-8') as f:
        old_data = f.read()
        print(old_data)

    # 准备新的文件夹名字
    old_file_name = os.path.splitext(file)  # 分割文件名字
    print('分割文件名字', old_file_name)

    new_file_name = old_file_name[0] + '[复制]' + old_file_name[-1]
    print(new_file_name)

    # 把数据写入到新的文件里面去
    with open(file='备份文件夹\\' + new_file_name, mode='w', encoding='utf-8') as f:
        f.write(old_data)

深浅拷贝

对象引用、浅拷贝、深拷贝(拓展、难点、重点)

Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果

其实这个是由于共享内存导致的结果

拷贝:原则上就是把数据分离出来,复制其数据,并以后修改互不影响。

先看 一个非拷贝的例子

使用=赋值(对象引用)

=赋值:数据完全共享

=赋值是在内存中指向同一个对象,如果是可变(mutable)类型,比如列表,修改其中一个,另一个必定改变

如果是不可变类型 (immutable) ,比如字符串,修改了其中一个,另一个并不会变

''' 对象引用 '''
array = [1, 2, 3, 4, 5]
arr1 = array  # 通过赋值的方式把对象进行了引用

# 这两个变量任然是同一对象
print(id(array))
print(id(arr1))

# 修改一个对象值,也导致另一个变量的结果也会改变
array[0] = 'a'

print(array)
print(arr1)

''' 浅拷贝 '''
# 浅拷贝:数据半共享(复制其数据独立内存存放,但是只拷贝成功第一层)
import copy  # 深浅拷贝模块,内置对象
array = [1, 2, 3, 4, 5]
# 浅拷贝会新建一个对象,区分原对象
arr1 = copy.copy(array) # 将 array 这个对象完全拷贝一份出来
print(id(array))
print(id(arr1))

''' 浅拷贝作用域 '''
array2 = [1, ['a', 'b', 'c'], 3]
arr2 = copy.copy(array2)
array2[1][0] = 100
print(array2)
print(arr2)
# 浅拷贝仅作用域一维数据,就是没有嵌套
# 如果浅拷贝数据中有嵌套数据,那么拷贝出来的内容中嵌套的数据部分任然指的是同一对象

''' 深拷贝 '''
# 深拷贝:数据完全不共享(复制其数据完完全全放独立的一个内存,完全拷贝,数据不共享)
# 深拷贝就是完完全全复制了一份,且数据不会互相影响,因为内存不共享
# copy.deepcopy 深拷贝,把对象里面各个维度的数据全部拷贝一份,区分原对象
array3 = [1, ['a', 'b', 'c'], 3]
arr3 = copy.deepcopy(array3)
array3[1][0] = 100
print(array3)
print(arr3)


'''
总结:
copy.copy 浅拷贝 - 只拷贝父对象,不会拷贝对象的内部的子对象。
copy.deepcopy 深拷贝 - 拷贝对象及其子对象
'''
案例 - 判断平年和闰年
"""
判断平年闰年:
	闰年的条件是: 满足其中一个条件就行
		一:能被4整除,但不能被100整除的年份(例如2008是闰年,1900不是闰年) 
		二:能被400整除的年份(例如2000年)也是闰年。
	反之都是平年。
	
请设计一个函数
	用户输入年份,传入函数中的参数
	在函数中判断输入的年份是平年还是闰年
"""
def max_year(year):
    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
        print(f'{year}是闰年')
    else:
        print(f'{year}是平年')


year = int(input('请输入你的年份:'))
max_year(year)
案例 - 深浅拷贝
"""
运行代码之后,请找出下面代码错误并且修正
提示:深浅拷贝,数据不重复
"""
import copy

data = {
    'cate': '童书馆',
    'sub_cate': None
}
sub_cate = ['科普百科', '儿童文学', '幼儿启蒙', '动漫卡通', '少儿英语']
all_cate = []  # 定义一个空的列表

# 在字典进行添加的时候,由于他们引用的是同一个对象,由于字典的键是相同的
# 会把前面的内容进行覆盖
for cate in sub_cate:
    data['sub_cate'] = cate  # 通过字典的键赋值
    print(id(data))
    # all_cate.append(data)  # 把字典添加到空的列表里面去
    # 浅拷贝,适用于一维维度
    all_cate.append(copy.copy(data))
    print('浅拷贝', id(copy.copy(data)))
    # 深拷贝,适用于所有维度
    all_cate.append(copy.deepcopy(data))
    print('深拷贝', id(copy.deepcopy(data)))
# print(all_cate)

文章来源:https://blog.csdn.net/weixin_43612602/article/details/134954631
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。