31天Python入门——第13天:文件操作

|----------------|
| 你好,我是安然无虞。 |

文章目录

    • 文件操作
      • [1. 文件的概念](#1. 文件的概念)
      • [2. python中的文件读取](#2. python中的文件读取)
      • [3. python中的文件写入](#3. python中的文件写入)
        • [补充: 文件指针相关](#补充: 文件指针相关)
      • [4. with语句实现文件读写](#4. with语句实现文件读写)
        • [扩展学习: json相关](#扩展学习: json相关)
      • [5. 补充练习](#5. 补充练习)

文件操作

1. 文件的概念

  1. 文件的概念和分类
    • 文件是用于存储数据的一种持久化媒介.
    • 在计算机中, 文件分为文本文件和二进制文件两种类型.
    • 文本文件由字符组成, 可以使用文本编辑器打开并阅读.
    • 二进制文件由字节组成, 包含非文本数据, 如图像、音频和视频等.

在日常生活, 工作中, 文件的操作主要包括: 创建文件, 打开, 关闭, 读, 写.

2. python中的文件读取

使用 open() 函数打开文件, 接受文件路径和打开模式, 编码等作为参数.
open函数会返回一个对象,我们可以称之为文件对象.

文件的打开,分为 文本模式 和 二进制模式:

通常,对文本文件,都是以文本模式打开
文本模式打开文件后,我们的程序读取到的内容都是字符串对象,写入文件时传入的也是字符串对象.

python 复制代码
f = open(file, mode, encoding)
# file: 需要打开的目标文件(包含路径和文件名), 类型是字符串.
# mode: 设置打开文件的模式. 读, 写, 追加写. 类型是字符串.
# encoding: 编码. 一般使用utf-8.

# 使用 close() 方法关闭文件, 释放资源.
f.close()
open函数的参数

要读写文件,首先要通过内置函数open 打开文件,获得文件对象.

函数open的参数如下:

python 复制代码
open(
    file, 
    mode='r', 
    buffering=-1, 
    encoding=None, 
    errors=None, 
    newline=None, 
    closefd=True, 
    opener=None
    )

其中下面这3个参数是我们常用的:

  • 参数 file

    file参数指定了要打开文件的路径.

    可以是相对路径,比如 'log.txt', 就是指当前工作目录下面的log.txt 文件 也可以是绝对路径,比如 'd:\project\log\log.txt'

  • 参数 mode

    mode参数指定了文件打开的 模式 ,打开文件的模式 决定了可以怎样操作文件

  • 参数 encoding

    encoding 参数指定了读写文本文件时,使用的 字符编解码 方式

    调用open函数时,如果传入了encoding参数值:

    • 后面调用write写入字符串到文件中,open函数会使用指定encoding编码为 字节串;
    • 后面调用read从文件中读取内容,open函数会使用指定encoding解码为字符串对象

    如果调用的时候没有传入encoding参数值,open函数会使用系统缺省字符编码方式. 比如在中文的Windows系统上,就是使用cp936(就是gbk编码).

    建议大家编写代码 读写文本文件时,都指定该参数的值.

常见的打开模式包括: 读取模式('r')、写入模式('w')、追加模式('a')和二进制模式('b')

上述代码中的f是open函数返回的文件对象.

以下是关于文件读取的常用模式.

模式 描述
'r' 默认模式, 以只读方式打开文件.文件的指针 将会放在文件的开头 .如果文件不存在, 将会引发FileNotFoundError异常.
'rb' 以二进制模式以只读方式打开文件.适用于读取二进制数据, 如图像、音频等.
'r+' 以读写方式打开文件.文件的指针将会放在文件的开头. 这里是不会清空文件内容的. 如果文件不存在, 将会引发FileNotFoundError异常.
'rb+' 以二进制模式以读写方式打开文件.文件的指针将会放在文件的开头.如果文件不存在, 将会引发FileNotFoundError异常.
python 复制代码
# 1. 相对路径.
# 2. 绝对路径.
# f:文件对象, 它是一个迭代器.
f = open(r".\test.png", 'rb', encoding='utf-8') # 读取图片,应该是二进制数据 
content = f.read()
print(content)

f.close()
文本的读取
  1. 使用 read() 方法读取整个文本文件的内容.
  2. 使用 readline() 方法逐行读取文本文件的内容, 每次调用 readline()方法, 它会返回文件中的下一行作为一个字符串.当到达文件末尾时, 它会返回一个空字符串. 可以在循环中使用 readline() 方法来逐行处理文件内容. -> 注意换行符也读取出来了
  3. readlines(): 这个方法用于一次性读取整个文件内容, 并将每一行作为一个字符串存储在一个列表中.它会返回一个包含所有行的列表, 每行作为一个元素. 你可以直接遍历这个列表或使用索引访问特定行.

以上3个方法都可以接收一个参数,它们接受一个可选的参数用于指定要读取的字符数或字节数(是字符数还是字节数和读取的文件类型相关联), 指定要读取的最大字符数或字节数.如果省略参数或指定为负数(默认值), 则会读取全部内容.返回值是一个包含读取行内容的字符串.

python 复制代码
# f:文件对象, 它是一个迭代器.
f = open(r".\test.txt", 'r', encoding='utf-8')
content = next(f)
print(content)
for text in f:
    print(f'文件内容: {text}')
# content = f.read(10)
content = f.readline(10) # 也是根据读取的字符数来的
content = f.readline(100) # 只将当前行的内容给出, 并没有给出下一行
# 对于readline()来说, 当读取的字符数小于当前行时截断显示, 当大于时也只是显示当前行的内容

print(content)
print(content)
lines = f.readlines(57)
print(lines)
line = True
while line:
    line = f.readline()
    print(line.strip())
f.close()

3. python中的文件写入

write() : 使用 write() 方法向文本文件写入文本内容, 调用write将内容写入到缓冲区.

flush() : 调用flush()将缓冲区的内容写入到磁盘.

close() : 调用close()关闭文件的同时也会将缓冲区的内容写入到磁盘. 所以调用close()的话, 可以不需要使用到flush().
以下是关于文件写入的常用模式.

r

w

a

b

+: 可读可写

思考一下: r+ 和 a 的区别: 后面会讲解到.

模式 描述
'w' 以写入方式打开文件.如果文件已存在, 则会被清空;如果文件不存在, 则会创建一个新文件.
'wb' 以二进制模式以写入方式打开文件.适用于写入二进制数据, 如图像、音频等.
'w+' 读写方式打开文件.如果文件已存在, 则会被清空;如果文件不存在, 则会创建一个新文件.
'wb+' 以二进制模式以读写方式打开文件.如果文件已存在, 则会被清空;如果文件不存在, 则会创建一个新文件.
'a' 以追加方式打开文件.文件的指针将会放在文件的末尾.如果文件不存在, 则会创建一个新文件.
'ab' 以二进制模式以追加方式打开文件.适用于追加二进制数据.
'a+' 以读写方式打开文件.文件的指针将会放在文件的末尾.如果文件不存在, 则会创建一个新文件.
'ab+' 以二进制模式以读写方式打开文件.文件的指针将会放在文件的末尾.如果文件不存在, 则会创建一个新文件.
python 复制代码
# 写入刚刚读取到的图片(二进制数据)
f = open('./jietu.png', 'wb')
f.write(b'xxxxxx')
f.close()

# 查看是否生成了这个图片, 双击图片是否正常显示
python 复制代码
# 追加写
f = open('./text.txt', 'a')
f.write('hello world\n') # 注意这个'\n'的处理, 可以在追加写的时候自动换行
f.close()
补充: 文件指针相关

LF 和 CRLF 是两种不同的行结束符,它们在不同操作系统和场景下有着不同的用途和特点:

  • LF 是 "Line Feed" 的缩写,表示换行符. 在文本文件中,它用于表示一行的结束,并将光标移动到下一行的开头. (\n)
  • CRLF 是 "Carriage Return and Line Feed" 的缩写,表示回车符和换行符的组合。它是由两个字符组成的,先是一个回车符(CR),将光标移动到当前行的开头,然后是一个换行符(LF),将光标移动到下一行的开头. (\r\n)
python 复制代码
f = open('./test.txt', 'a')
n = f.tell() # n为当前文件指针所在的位置
print(n)

# test.txt 内容是'hello world'一行内容
# 用LF其实就是 'hello world\n'
# 用CRLF其实就是 'hello world\r\n'

# 如果将上面的'a'换成'r'的话: 打印出来的n就是0, 因为以r的方式打开文件会将文件指针放到文件的开头

现在有一个需求, 将文件中的'world'换成'python', 需要怎么做: 我们可以手动操作文件指针

python 复制代码
f = open('./test.txt', 'a')
f.seek(6) # 将指针移动到第6个字符处
f.truncate() # 截断 - 将文件指针后的字符全部截掉
f.write('python\n') # 这样就可以将原来的 hello world 改为 hello python
f.close()

4. with语句实现文件读写

with 语句可以自动管理文件的打开和关闭, 无需显式调用 close() 方法.
示例:

python 复制代码
# as关键字后面的变量f实际上就是文件对象
with open('test.txt', 'r') as f:
	content = f.read()

print(content)

使用with语句可以更安全和高效地进行文件读写操作, 并确保在读写完成后正确地关闭文件.无需手动调用close()方法.

注意:
with 语句本身不会限制变量的作用域. 变量的作用域取决于它被定义的位置(全局作用域或局部作用域)

  • 如果 with 语句块位于全局作用域中,那么在 with 语句块中定义的变量(如 content)也是全局变量,可以在整个模块中访问.
  • 如果 with 语句块位于函数内部,那么在 with 语句块中定义的变量是局部变量,只能在该函数内部访问.

所以上面的代码中可以在with语句外使用content变量.

扩展学习: json相关

JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。

它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。

易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

  • 在Python中,JSON可以表示为字符串,因为在传输或存储时,JSON数据需要以字符串的形式进行序列化.
  • 将Python对象转换为JSON字符串以便传输或存储. 然后,在需要时,可以将JSON字符串反序列化为Python对象.

JSON的语法规则与Python中的字典(dict)非常相似,因此我们可以将Python中的字典转换为JSON格式,以便在不同的系统或应用程序之间进行数据交换.

总结起来,JSON是一种数据交换格式,可以用于表示数据,并且在Python中可以进行JSON与Python对象之间的相互转换.

  • json.loads(): 将json字符串转化为字典对象
python 复制代码
# json.loads(): 将json字符串转化为字典对象

json_string = """
{
  "name": "John",
  "age": 25,
  "xxx": null,
  "isStudent": true,
  "hobbies": ["reading", "playing", "traveling"],
  "address": {
    "street": "123 Main St",
    "city": "New York"
  },
  "scores": [85, 92, 78, 90]
}
"""

import json

data_dict = json.loads(json_string) # 将json字符串转化为字典对象
print(type(data_dict)) # <class 'dict'>
print(data_dict) # {'name': 'John', 'age': 25, 'xxx': None, 'isStudent': True, 'hobbies': ['reading', 'playing', 'traveling'], 'address': {'street': '123 Main St', 'city': 'New York'}, 'scores': [85, 92, 78, 90]}
score = data_dict.get('scores')
print(f'分数是: {score}') # 分数是: [85, 92, 78, 90]
  • json.dumps(): 将字典对象转化为json字符串
python 复制代码
# json.dumps(): 将字典对象转化为json字符串

import json

data_dic = {
    "name": "张三",
    "age": 25,
    "xxx": None,
    "isStudent": True,
    "hobbies": ["reading", "playing", "traveling"],
    "address": {
    "street": "123 Main St",
    "city": "New York"
    },
    "scores": [85, 92, 78, 90]
}
with open("./test.txt", 'w', encoding='utf8') as f:
    f.write(json.dumps(data_dic, ensure_ascii=False)) # json.dumps() 将字典对象转化为json字符串

5. 补充练习

1.实现一个通讯录的功能. 存储的联系人需要永久存储. (提示: 将联系人存储到文件.)

通讯录的基本功能都要具备.

  1. 添加联系人.
  2. 删除联系人.
  3. 查询指定联系人.
  4. 查看所有的联系人.
  5. 退出通讯录.
python 复制代码
import json

def query_all():
    with open("./contact.txt", 'r', encoding='utf8') as f:
        content = f.read()
        if content:
            # 如果存在, 就要展示所有的联系人.
            print('为您查询到的所有联系人如下:')
            json_data = json.loads(content)
            for key, val in json_data.items():
                print(f'{key}: {val}')
        else:
            print(f'该通讯录目前没有联系人, 请先添加联系人')

def query_contact():
    name = input("请输入想要查询的联系人姓名:")
    with open("./contact.txt", 'r', encoding='utf8') as f:
        content = f.read()
        if content:
            # 如果存在, 就要做查询操作.
            json_data = json.loads(content)
            phone = json_data.get(name)
            print(f'为您查询到的指定联系人{name}的电话是: {phone}')
        else:
            print(f'未找到指定联系人: {name}')

def del_contact():
    name = input("请输入想要删除的联系人姓名:")
    with open("./contact.txt", 'r+', encoding='utf8') as f:
        content = f.read()
        if content:
            # 如果存在, 就要做删除操作.
            json_data = json.loads(content)
            json_data.pop(name)
            f.seek(0)
            f.truncate()
            f.write(json.dumps(json_data, ensure_ascii=False))
        else:
            # 如果不存在, 就没办法删了.
            print(f'未找到指定联系人: {name}')

def add_contact():
    name = input("请输入联系人姓名:")
    phone = input("请输入联系人电话:")
    contact_dict = {
        name: phone
    }
    # 将联系人信息存储到文件 - 可以使用字典
    with open("./contact.txt", 'r+', encoding='utf8') as f:
        content = f.read() # 此时文件指针跑到了文件内容的末尾
        if content:
            json_data = json.loads(content)
            json_data.update(contact_dict)
        else:
            json_data = contact_dict
        # 将文件指针移动到文件开头
        f.seek(0)
        f.truncate()
        f.write(json.dumps(json_data, ensure_ascii=False))


def menu():
    print("欢迎使用通讯录管理系统")
    print("------welcome-----")
    print("菜单选项:")
    print("1. 添加联系人.")
    print("2. 删除联系人.")
    print("3. 查询指定联系人.")
    print("4. 查看所有的联系人.")
    print("5. 退出通讯录.")
    while True:
        choice = int(input("请输入您想要操作的选项:"))
        if choice == 1:
            add_contact()
        elif choice == 2:
            del_contact()
        elif choice == 3:
            query_contact()
        elif choice == 4:
            query_all()
        elif choice == 5:
            print('已退出通讯录管理系统')
            break

menu()

2.图片文件类型识别

你们公司有一批图片文件,不小心被管理人员把扩展名都去掉了.

这批图片文件中有的是png文件,有的是jpg文件.

png文件的开头一定是 这样的 89 50 4e 47 0d 0a 1a 0a 8个字节

现在要求你写一个函数,参数是图片文件的路径,函数根据文件的开头8个字节的信息,判断该文件是不是png文件.

如果是,打印出 png, 否则打印出 jpg.

python 复制代码
def tellPngFile(fp):
	f = open(fp, 'rb')
	content = f.read(8) # 只读出来文件开头8个字节即可
	f.close()
		
	if content == b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a':
		print('png')
	else:
		print('jpg')

tellPngFile('download.png')

|----------------------|
| 遇见安然遇见你,不负代码不负卿。 |
| 谢谢老铁的时间,咱们下篇再见~ |

相关推荐
猫头虎7 分钟前
Rust评测案例:Rust、Java、Python、Go、C++ 实现五大排序算法的执行时间效率比较(基于 OnlineGDB 平台)
java·开发语言·c++·python·golang·rust·排序算法
爱吃烤鸡翅的酸菜鱼8 分钟前
【Java】基于策略模式 + 工厂模式多设计模式下:重构租房系统核心之城市房源列表缓存与高性能筛选
java·redis·后端·缓存·设计模式·重构·策略模式
milanyangbo10 分钟前
从局部性原理到一致性模型:深入剖析缓存设计的核心权衡
开发语言·后端·缓存·架构
IT_陈寒10 分钟前
SpringBoot实战避坑指南:我在微服务项目中总结的12条高效开发经验
前端·人工智能·后端
恒风521212 分钟前
实时显示鼠标的坐标值,注意事件的(event)
python·信息技术类·对口高考
JaguarJack13 分钟前
Laravel ObjectId 性能最强体积最小的分布式 UUID 生成扩展
后端·laravel
ftpeak22 分钟前
Rust 嵌入式开发的经验之谈
开发语言·后端·rust
Victor35624 分钟前
Redis(119)Redis的安全机制如何配置?
后端
lly20240627 分钟前
Node.js 多进程
开发语言
Victor35630 分钟前
Redis(118)Redis的缓存机制如何实现?
后端