6、Python入门——文件操作



前言

  • 内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索,引入了文件的概念。
  • 在日常生活中,文件操作主要包括打开、关闭、读、写、备份等操作。

一、读文件

"""
1、涉及到的函数:
        open(文件路径, 模式, 码表)
            参1: 文件路径, 可以写绝对路径, 也可以写相对路径.
                绝对路径: 固定的, 写死的路径, 以盘符开头.   
                相对路径: 一般是相对于当前项目的路径来讲的.
            
            参2: 模式
                r:  只读, 默认模式, 可以读取 字符.  '你好'
                rb: 只读, 以二进制形式读取数据, 例如: 图片, 音频, 视频, 文本...
                w: 只写(覆盖写入), 可以操作 字符.
                wb: 只写(覆盖写入), 操作的是 字节.
                a: 追加写入, 可以操作 字符.
                ab: 追加写入, 操作的是 字节.

            参3: 码表(针对中文有效)
                码表 = 字符 + 其对应的int类型的整数, 例如: 'a' => 97, 'A' => 65, '0' => 48

        read(num)       一次读取num个字节的数据, 不写num, 则一次性读取全部数据.  如果文件不存在就报错.
        readlines()     一次性读取所有的行, 并把每行数据封装成1个字符串, 然后整体放到1个列表中, 即: [第一行数据, 第二行数据...]
        readline()      一次读取一行数据, 并封装成字符串, 然后返回.

        close()         关闭文件, 释放资源.
2、注意:
    1. 上述的模式中, r, w, a 都是可以操作字符的, 即: 可以结合码表 encoding 一起使用.
    2. 上述的模式中, rb, wb, ab都是操作二进制的, 所以不能结合 encoding参数 一起使用.
    3. 码表 就是描述 字符 和 整数之间的关系的, 例如: ASCII, GBK, utf-8...
    4. 在gbk码表中, 1个中文占2个字节, 在utf-8码表中, 1个中文占3个字节.
       数字, 字母, 特殊符号无论在什么码表中, 都只占 1个字节.

3、细节:
	1. Python中, 路径的写法, 要么用 \\,  要么用 /, 要么用 r'一个\就行', 即: r'\' 会取消 \的转移含义, 当做1个普通字符来用.
    2. 相对路径默认是相对于 当前项目的路径来写的, 即: 你直接写 1.txt, 想到于是  /当前项目路径/1.txt
"""
  • mode访问模式详解
模式描述
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式
r+打开一个文件用于读写。文件指针将会放在文件的开头
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头
w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件
w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写

1. 文件的路径写法

代码如下:

# 1. 打开文件.
# 相对路径.
f = open('1.txt', 'r')

# 绝对路径
# 写法1: r''   取消\的特殊含义
f = open(r'D:\workspace\pythonProject\day07\1.txt', 'r')
# 写法2: \\ => \
f = open('D:\\workspace\\pythonProject\\day07\\1.txt', 'r')
# 写法3: 直接写 1个 /
f = open('D:/workspace/pythonProject/day07/1.txt', 'r')

# 如果数据源文件不存在, 就会报错.
f = open('2.txt', 'r')  # 如果项目中不存在2.txt文件就会报错

# ./代表当前项目的路径, 可以省略不写.
# f = open('./data/2.txt', 'r')
f = open('data/2.txt', 'r')


# 最后一定记得关闭文件.
f.close()

2. 读文件的函数

1. 按长度读取文件

代码如下:

# read()函数
# 1. 读取文件.
# data = f.read()     # 不写长度, 一次性读取所有的内容.
# data = f.read(2)      # 写长度, 则一次读取 2个字节.
# data = f.read(3)      # 写长度, 则一次读取 3个字节.
data = f.read(10)       # 写长度, 则一次读取 10个字节, 如果文件内容不足10个字节, 则读取全部内容.
print(data)

# 文件数据已经读取完毕, 如果继续读取, 则: 返回 ''
data2 = f.read(3)
print(data2)

# 2. 关闭文件.
f.close()

2. 按行读取文件

"""
涉及到的函数:
    readlines()     一次性读取所有的行, 并把每行数据封装成1个字符串, 然后整体放到1个列表中, 即: [第一行数据, 第二行数据...]
    readline()      一次读取一行数据, 并封装成字符串, 然后返回.
"""

代码如下:

# 1. 打开文件.
f = open('1.txt', 'r')  # 相对路径写法

# 2. 读取文件数据.
# 方式1: readline(), 一次读取一行, 分解版写法.
line1 = f.readline()    # 'abc\n'
line2 = f.readline()    # 'def\n'
line3 = f.readline()    # 'g\n'
line4 = f.readline()    # ''

print(f'line1: {line1}', end='')
print(f'line2: {line2}', end='')
print(f'line3: {line3}', end='')
print(f'line4: {line4}', end='')

# 方式2: readline(), 一次读取一行, 合并版写法.
while True:
    # 一次读取一行数据.
    line = f.readline()
    # 判断读取到的数据是否为空, 如果为空, 说明文件内容读取完毕, 结束循环即可.
    # if len(line) == 0:
    # if not line:              # 0, None, '' => False, 其它 => True
    # if bool(line) == False:   # 0, None, '' => False, 其它 => True
    if line == '':
        break
    # 走到这里, 说明读取到数据了.
    print(f'line: {line}', end='')

# 方式3: readlines(): 一次读取所有行, 每行封装成字符串, 然后整体封装成列表.
lines = f.readlines()
print(lines)        # ['abc\n', 'def\n', 'g\n']
print(type(lines))  # <class 'list'>

# 3. 关闭文件.
f.close()

3. 读取文件中的中文

"""
1、中文 解释:
    计算机底层存储, 操作, 运算数据, 都是采用数据的 二进制(补码)形式, 所以中文, 特殊符号, 数字, 英文字母底层都是要转成二进制的.
    后来科学家就提出了 码表的概念, 用来描述 字符 及其 对应的数字的关系.  例如: 'a' => 97,  'A' => 65, '0' => 48...
    最早的码表 ASCII码表记录的就是: 英文字母, 数字, 特殊符号及其对应的 数字的关系.
    后来随着计算机的普及, 各个国家都有了各个国家的 码表, 咱们国内使用最多的主要是 GBK系列, 1个中文 占 2个字节.
    后来有个组织就统计全世界各个国家的码表, 制定了一张"万国码", 也叫"统一码", 这就是: Unicode系列的码表, 例如: utf-8, utf-16, utf-32...

2、总结:
    国内主要用 GBK码表, 1个中文占 2个 字节.
    国际通用码表 UTF-8, 1个中文占 3个字节.
    无论是什么码表, 英文字母, 数字, 特殊符号都只占1个字节.
    只要以后你遇到了乱码的情况, 不用想, 原因只会有1个: 编解码不一致.
"""

代码如下:

# 读取中文要设计码表为utf-8

# 1. 打开文件.
f = open('1.txt', 'r')        # 默认的码表是: gbk码表.
f = open('1.txt', 'r', encoding='gbk')    # 效果同上.

# 码表名可以写 utf-8 或者 utf8, 建议写: utf-8
f = open('1.txt', 'r', encoding='utf8')

# rb: 以 二进制形式 读取文件, 报错, 二进制方式(无论读写), 不能结合 码表(encoding参数)一起用.
f = open('1.txt', 'rb', encoding='utf8')

# 正确的 二进制 写法.
f = open('1.txt', 'rb')

# 2. 读取文件数据.
lines = f.readlines()
print(lines)

# 3. 关闭文件.
f.close()

二、写文件

"""
1、涉及到的函数如下:
      write(数据)           一次往文件中写入指定的内容, 不能是列表, 元组等多个值...
      writelines(容器类型)   一次往文件中写入多个值, 必须是: 容器类型.

2、细节:
      1. 从打开 到 关闭, 这是一个完整的动作, 其内部是不会不断覆盖的.
      2. 以后我们用 read(), readline() + write() 这种方式较多.
         而 readlines() + writelines() 这种方式相对较少, 它比较适合 小文件的操作.
"""

1. 注意写数据的模式, w: write,覆盖写入. a: append,追加写入.
2. 读的时候, 如果 数据源文件不存在, 会报错. No Such File Or Directory....
3. 写的时候, 如果 目的地文件不存在, 会自动创建.

代码如下:

# 场景1: 字符方式 覆盖写入, 或者追加写入, 即: w, a 模式演示.
# 1. 打开文件.
# 只读模式, 如果文件不存在, 就报错.
f = open('2.txt', 'r')

# 覆盖写入模式 或者 追加写入模式, 如果目的地文件不存在(前提: 父目录存在), 则会自动创建.
f = open('2.txt', 'w')
f = open('2.txt', 'a')

# 父目录存在
f = open('./data/1.txt', 'w')   # 覆盖写入.
f = open('./data/1.txt', 'a')   # 追加写入.

f = open('./data/1.txt', 'w', encoding='utf-8')   # 覆盖写入, 支持中文.

# 父目录不存在的情况.
f = open('aa/bb/cc/2.txt', 'w')     # FileNotFoundError, 文件不存在.

# 2. 往文件中写数据.
f.write('abc\n')
f.write('def\n')
f.write('g\n')
f.write('好好学习!\n')  # 如果要写中文, 记得加上 encoding='utf-8'

# 3. 关闭文件.
f.close()

三、复制文件

1. 拷贝文件

代码如下:

# 需求1: 把 data目录下的1.txt => data目录下的2.txt文件中.

# # 1. 封装数据源文件, 获取: 文件对象.
src_f = open('data/1.txt', 'r', encoding='utf-8')
# # 2. 封装目的地文件, 获取: 文件对象.
dest_f = open('data/2.txt', 'w', encoding='utf-8')

# 3. 具体的拷贝动作.
# # 思路1: 一次读取所有的文件数据, 然后一次性写到目的地文件中.
data = src_f.read()
dest_f.write(data)

# # 思路2: 一次读取所有的行, 然后一次性写到目的地文件中.
lines = src_f.readlines()
dest_f.writelines(lines)

# 思路3: 循环读取, 一次读取指定数量的数据, 然后写出到目的地文件中.
while True:
#     # 一次读取指定数量的数据, 一般是: 1024 的整数倍.
#     # bit(比特位), byte, kb, mb, gb, tb, pb, eb, zb, yb, bb, nb, db
	data = src_f.read(8192)   # 一次读取 8KB
#     # 判断读取到的数据是否为空, 如果为空, 说明文件读完了, 结束循环即可.
    if not data:    # '' => False
          break
#   # 走这里, 说明读取到数据了, 就写到目的地文件中.
    dest_f.write(data)
#
# 4. 释放资源.
src_f.close()
dest_f.close()


# 需求2: 把 data目录下的a.jpg => data目录下的b.jpg文件中.
# 1. 封装数据源文件, 获取: 文件对象.
src_f = open('data/a.jpg', 'rb')

# 2. 封装目的地文件, 获取: 文件对象.
dest_f = open('data/b.jpg', 'wb')

# 3. 具体的拷贝动作.
# 思路3: 循环读取, 一次读取指定数量的数据, 然后写出到目的地文件中.
while True:
    # 一次读取指定数量的数据, 一般是: 1024 的整数倍.
    # bit(比特位), byte, kb, mb, gb, tb, pb, eb, zb, yb, bb, nb, db
    data = src_f.read(8192)   # 一次读取 8KB
    # 判断读取到的数据是否为空, 如果为空, 说明文件读完了, 结束循环即可.
    if not data:    # '' => False
        break
    # 走这里, 说明读取到数据了, 就写到目的地文件中.
    dest_f.write(data)

# 4. 释放资源.
src_f.close()
dest_f.close()

2. 备份文件

代码如下:

# 需求: 键盘录入 当前目录下任意的1个文件名, 然后对该文件进行备份, 备份文件名格式为: 原文件名[备份].原后缀名, 例如:  test.txt => test[备份].txt

# 1. 提示用户录入要备份的 文件名, 并接收.       绕口令.txt,  abc.mp3.txt,   .txt
old_name = input('请录入要备份的文件名: ')

# 2. 找到 最后1个 . 的索引.
index = old_name.rfind('.')    # 因为rfind是找到最后一次出现的位置, 找不到返回 -1

# 3. 判断文件名是否合法.
# 3.1 如果合法, 就拷贝.
if index > 0:
    # 4. 根据原文件名, 拼接 新文件名.           绕口令.txt  => 绕口令[备份].txt
    new_name = old_name[:index] + '[备份]' + old_name[index:]
    # print(new_name)

    # 5. 正常的读写操作.
    # 5.1 打开 数据源文件.
    # old_f = open(old_name, 'r', encoding='utf-8')   # 码表性质只能拷贝 纯文本文件.
    old_f = open(old_name, 'rb')        # 二进制形式读写, 无需指定码表, 通用版读写.
    # 5.2 打开 目的地文件.
    # new_f = open(new_name, 'w', encoding='utf-8')
    new_f = open(new_name, 'wb')
    # 5.3 循环 拷贝.
    while True:
        # 5.4 每次读取 8192个 字节的数据, 存储到 data变量中.
        data = old_f.read(8192)
        # 5.5 判断, 如果没有读取到内容, 说明文件内容读取完毕, 结束拷贝.
        if len(data) <= 0:
            break
        # 5.6 走到这里, 说明读取到内容, 将读取到的数据写出到 目的地文件中.
        new_f.write(data)
    # 5.7 释放资源.
    old_f.close()
    new_f.close()
    print('备份成功!')

else:
    # 3.2 如果不合法, 就提示, 然后程序结束.
    print('您录入的路径有误, 程序结束!')

3. 借助OS模块实现文件备份

"""
os 模块介绍:
    概述:
        全称叫Operating System, 也叫: 系统模块, 主要是操作 文件, 文件夹, 路径等的.
    常用的函数:
        getcwd()    获取当前工作目录, 即: 你写的相对路径, 都是相对于这个路径来讲的. get current work directory
        chdir()     改变工作路径, 即: 相对路径以后就是相对于这个路径来讲的.
        listdir()   查看当前目录下, 所有的子级(不包括子级的子级)
        mkdir()     创建目录的, 如果存在就报错, 不存在就创建.
        rename()    修改文件的名字.
        remove()    删除指定的文件.

扩展: shutil模块
    它里边的函数, 大多数是和 文件(文件夹)相关的, 且大多数的函数 底层都支持递归.
    例如: copyfile() 就可以实现 拷贝文件.
"""

代码如下:

# 导包
import os

# 演示os模块常用的函数.
# 1. 获取当前工作目录, 即: 你写的相对路径, 都是相对于这个路径来讲的.
print(os.getcwd())  # D:\workspace\ai_30_basic_bj\pythonProject\day07

# 2. 设置当前的工作空间为指定的目录.
# os.chdir('d:\\data\\')
os.chdir('d:/data/')

# 重新查看当前的工作空间.
print(os.getcwd())

# 读取文件内容.
f = open('1.txt', 'r', encoding='utf-8')
print(f.read())
f.close()

# 3. 查看当前目录下所有的文件(不包括子级的子级)
print(os.listdir('./'))     # 获取当前目录下所有的文件(不包括子级的子集)

# 4. 在指定目录下创建 子目录.
os.mkdir('./data/ai30')  # make directory: 制作目录, 该目录必须:不存在.

# 5. 改名.
os.rename('data/ai30', 'data/ai30_new')

# 6. 删除文件的.
os.remove('data/ai30_new/a.jpg')


# 扩展: shutil 模块.
import shutil

# shutil模块的 copyfile()函数可以实现: 拷贝文件.
# shutil.copyfile('data/1.txt', 'data/2.txt')
shutil.copyfile('data/a.jpg', 'data/b.jpg')


# 大白话: 把文件 -> 目录下.
# 需求: 把 当前项目下的 data/1.txt 或者 data/a.jpg 拷贝到  d:/data/ 目录下, 且文件名要保持一致.

四、拓展语法(with - open)

代码如下:

"""
1、概述:
      它主要是针对于 文件操作的, 即: 你再也不用手动 close()释放资源了, 该语句会在 语句体执行完毕后, 自动释放资源.

2、格式:
      with open('路径', '模式', '码表') as 别名,  open('路径', '模式', '码表') as 别名:
            语句体

3、特点:
      语句体执行结束后, with后边定义的变量, 会自动被释放.
"""

# 1. 打开 数据源 和 目的地文件.
with open('./data/a.txt', 'rb') as src_f, open('./data/b.txt', 'wb') as dest_f:
    # 2. 具体的 拷贝动作.
    # 2.1 循环拷贝.
    while True:
        # 2.2 一次读取8192个字节.
        data = src_f.read(8192)
        # 2.3 读完了, 就不读了.
        if len(data) <= 0:
        # if data == '':
            break
        # 2.4 走到这里, 说明读到了, 把读取到的数据写出到目的地文件.
        dest_f.write(data)

五、补充编解码

代码如下:

"""
1、概述:
      编码: 把我们看得懂的数据 => 我们看不懂的数据, 这个动作叫: 编码.
      解码: 把我们看不懂的数据 => 我们看得懂的数据, 这个动作叫: 解码.
2、细节:
      1. 中文 => gbk(2个字节), UTF-8(3个字节),  数字,字母,特殊符号 => 无论什么码表, 都只占 1个 字节.
      2. 只要出现乱码问题了, 原因只有1个: 编解码不一致.
      3. 编解码涉及到的函数:
           encode(): 编码.
           decode(): 解码.
      4. 二进制的特殊写法, b'内容', 只针对于 数字, 字母, 特殊符号有效, 针对于 中文 无效.
"""
# 1. 演示 编码.
# s1 = 'abcXYZ123!@#'
s1 = 'aX1!你好'

# 具体的编码动作.
bys1 = s1.encode()                  # 默认码表(utf-8).  bys => bytes, 字节列表.
bys2 = s1.encode(encoding='gbk')    # 指定码表 => gbk
bys3 = s1.encode(encoding='utf-8')  # 指定码表 => utf-8

print(f'bys1: {bys1}')  # b'aX1!\xe4\xbd\xa0\xe5\xa5\xbd'
print(f'bys2: {bys2}')  # b'aX1!\xc4\xe3\xba\xc3'
print(f'bys3: {bys3}')  # b'aX1!\xe4\xbd\xa0\xe5\xa5\xbd'

# 打印类型
print(type(bys1))   # <class 'bytes'>
print(type(bys2))   # <class 'bytes'>
print(type(bys3))   # <class 'bytes'>

# 2. 演示 解码
s2 = bys1.decode()                  # 默认码表(utf-8).
s3 = bys1.decode(encoding='gbk')    # 指定gbk码表
s4 = bys1.decode(encoding='utf-8')  # 指定utf-8码表

print(f's2: {s2}')
print(f's3: {s3}')  # 乱码, 因为: 编解码不一致.
print(f's4: {s4}')

print(type(s2))
print(type(s3))
print(type(s4))


# 3. 演示 二进制的特殊写法.
bys4 = b'abcXYZ123!@#'
# bys4 = b'abcXYZ123!@#你好'    # 报错, b'内容' 只针对于 数字, 字母, 特殊符号有效, 针对于 中文 无效.
print(f'bys4: {bys4}')  # b'abc'
print(type(bys4))       # <class 'bytes'>


总结

  • 以上就是今天要分享的Python中简单的文件操作,后续会持续更新其他文件操作。
  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值