[TOC]
## **文件读写模式概述**
### `r` 读文件模式
* r 只读模式
普通读模式,不能写入,不指定编码的话默认使用utf-8编码读取文件
* r+ 读写模式
可以写入,但默认只能将新内容写到文件最后面,可以理解为追加
* rb 二进制读模式
数据读到内存里直接是bytes格式,主要针对图片,视屏等无编码文件
### `w` 写文件模式
* w 只写模式
只能写不能读,且是新创建文件来写,如果指定的文件存在,文件内容会先被清空
* w+ 写读模式
能写能读,也是新创建文件来写,且只能读新写入的内容,没什么用
* wb 二进制写模式
二进制写模式,不能读,新建文件来写,存在的话会先清空
### `a` 追加模式
* a 追加模式
只能往文件中追加内容,不能读也不能写
* ab 二进制追加模式
写入时需要直接传入以某种编码的0100101,即:字节类型
## **`r`模式详解**
### r 只读模式
* 语法案例
```
f = open(file='D:/dir/file.txt',mode='r',encoding='utf-8')
data = f.read()
f.close()
```
* 语法解释:
```
file='D:/dir/file.txt' 文件路径
mode='r' 模式=只读
encoding='utf-8' 该文档使用的编码
f.read() 读取所有内容,内容是已经转换完毕的字符串。
f.close() 表示关闭文件
```
>PS: 此处的encoding必须和文件在保存时设置的编码一致,不然会造成乱码。
file、mode、encoding关键字都可以省略,如`('file.txt','r','utf-8')`
### r+ 读写模式
* 语法案例
```
f = open("test.txt",'r+',encoding="utf-8")
data = f.read() #读内容
print(data)
f.write("\ngirl 河北") #写内容
f.close()
```
>上面的内容写到哪个位置了呢?答案是追加到了最后面。
### rb 二进制读模式
* 语法案例
```
f = open(file='D:/firedir/test.txt',mode='rb')
data = f.read()
f.close()
```
* 语法解释:
```
file='D:/firedir/test.txt' 文件路径
mode='rb' 只读(可以修改为其他)
f.read() 读取所有内容
f.close() 关闭文件
```
> 二进制读模式打开文件时并未指定encoding,是因为直接以rb模式打开了文件 ,数据读到内存里直接是bytes格式,如果想看内容,还需要手动decode,所有在文件打开阶段,不需要指定编码
### chardet 获取文件编码
当一个要处理的文件不知道编码,却要打开这个文件,可以用这个方法获取文件的编码
```
import chardet
f = open('log',mode='rb')
data = f.read()
f.close()
result = chardet.detect(open('log',mode='rb').read())
print(result)
输出:
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
```
## **`w` 模式详解**
### w 只写模式
* 语法案例
```
f = open(file='D:/firedir/test.txt',mode='w',encoding='utf-8')
f.write('北大,微信号:xxxxx')
f.close()
```
* 语法解释:
```
file='D:/firedir/test.txt' 表示文件路径
mode='w' 表示只写
encoding='utf-8' 将要写入的unicode字符串编码成utf-8格式
f.write(...) 表示写入内容
f.close()
```
### wb 二进制写模式
* 语法案例
```
f = open(file='D:/firedir/test.txt',mode='wb')
f.write(b'm\xe7\xbd\x97')
f.write('n钢'.encode('utf-8'))
f.close()
```
* 语法解释:
```
file='D:/firedir/test.txt' 文件路径
mode='wb' 只以2进制模式写
f.write(b'm\xe7\xbd\x97') 写入字节内容(utf8编码的`m罗`)
f.write('n钢'.encode('utf-8')) 写入普通内容并指定转码为utf8
f.close()
```
>注意:wb,写入时指定编码方式的二进制内容,或当场用encode方法转码
### w+ 写读模式
* 语法案例
```
f = open("test.txt",'w+',encoding="utf-8")
print('--1',f.read(),'1--')
f.write("哈哈")
print("--2",f.read(),"2--")
f.seek(0)
print("--3",f.read(),"3--")
f.close()
```
* 输出和解释
```
--1 1--
--2 2--
--3 哈哈 3--
```
>注意第一行两个标记1中间是空的,代表根本没读到内容,因为先清空了文件
第二行的标记之间也是空的,因为写入数据后,光标指向了最后,
第三行重新指定了光标,才能读出来东西,这功能没什么用
## **`a` 追加模式详解**
### a 追加模式
* 语法案例
```
f = open("test.txt",'a',encoding="utf-8")
f.write("\n杜姗 京")
f.close()
```
* 运行结果
```
姓名 地区
杜姗 京
```
>注意:
文件操作时,以 “a”或“ab” 模式打开,则只能追加,即:在原来内容的尾部追加内容
### ab 二进制追加
* 语法案例
```
f = open("test.txt",'ab')
f.write(b'm\xe7\xbd\x97')
f.write('n钢'.encode('utf-8'))
f.close()
```
* 语法解释:
```
mode='ab' 只以2进制模式追加
f.write(b'm\xe7\xbd\x97') 写入字节内容(utf8编码的`m罗`)
f.write('n钢'.encode('utf-8')) 写入普通内容并指定转码为utf8
f.close()
```
>注意:wb,写入时指定编码方式的二进制内容,或当场用encode方法转码
## **其它方法简述**
### fileno 返回文件句柄索引
`fileno(self, *args, **kwargs)`
返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到
### flush 强制刷写至磁盘
`flush(self, *args, **kwargs)`
把文件从内存buffer里强制刷新到硬盘
### readable 是否可读
`readable(self, *args, **kwargs)`
判断是否可读,可读为True,linux中,一切皆文件,但如设备类文件就不可读
### readline 读一行
`readline(self, *args, **kwargs)`
只读一行,遇到\r or \n为止
### seek 移动光标
`seek(self, *args, **kwargs)`
把操作文件的光标移到指定位置
**注意:**
seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。
如“路飞学城” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“飞”和“学”两个字中间。
但如果是utf8,seek(4)会导致,拿到了飞这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节
### seekable 是否可以移动光标
`seekable(self, *args, **kwargs)`
判断文件是否可进行seek操作
### tell 返回光标位置
`tell(self, *args, **kwargs)`
返回当前文件操作光标位置
### truncate 截断文件
`truncate(self, *args, **kwargs)`
按指定长度截断文件
指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。
### writable 判断是否可写
`writable(self, *args, **kwargs)`
判断文件是否可写
## **循环读取一行**
### 用for循环
```
f = open("test.txt",'r',encoding="utf-8")
for line in f:
print(line)
f.close()
```
### 用readline
```
f = open("test.txt",'r',encoding="utf-8")
print(f.readline())
print(f.readline())
f.close()
```
## **如何修改文件**
### 硬盘的存储原理
当你把文件存到硬盘上,就在硬盘上划了一块空间,存数据,等你下次打开这个文件 ,seek到一个位置,每改一个字,就是把原来的覆盖掉,如果要插入,是不可能的,因为后面的数据在硬盘上不会整体向后移。所以就出现你想插入,却变成了会把旧内容覆盖掉。
如果要修改,就有两种方法:
1. 占内存方法
把内容全部读到内存里,数据在内存里可以随便增删改查,修改之后,把内容再全部写回硬盘,把原来的数据全部覆盖掉,但如果文件很大,可能会把内存撑爆
2. 占硬盘方法
如果不想占内存,只能边读边改,就是打开旧文件的同时,生成一个新文件,边从旧的里面一行行的读,边往新的一行行写,遇到需要修改就改了再写到新文件 ,这样内存里一直只存一行内容。改完后,再把旧的覆盖掉,但在改的过程中,还是有2份数据。
### 案例:
修改黄鹤楼这首诗的第三行为'哈哈哈',文件名为`my.txt`,诗如下:
```
昔人已乘黄鹤去,此地空余黄鹤楼。
黄鹤一去不复返,白云千载空悠悠。
晴川历历汉阳树,芳草萋萋鹦鹉洲。
日暮乡关何处是?烟波江上使人愁。
```
#### 占硬盘方式代码示例
```python
import os
f_name = "my.txt"
f_new_name = "%s.new" % f_name
old_str = "晴川历历汉阳树,芳草萋萋鹦鹉洲"
new_str = "哈哈哈"
f = open(f_name,'r',encoding="utf-8")
f_new = open(f_new_name,'w',encoding="utf-8")
for line in f:
if old_str in line:
new_line = line.replace(old_str,new_str)
else:
new_line = line
f_new.write(new_line)
f.close()
f_new.close()
os.rename(f_new_name,f_name) #把新文件名字改成原文件的名字,,windows使用os.replace
```
#### 占内存方式代码示例1
这种方法用字符串替换方式实现
```python
f_name = "my.txt"
old_str = "晴川历历汉阳树,芳草萋萋鹦鹉洲"
new_str = "哈哈哈"
f = open(f_name,'r+',encoding="utf-8")
data=f.read()
data=data.replace(old_str,new_str)
f.seek(0)
f.truncate()
f.write(data)
f.close()
```
#### 占内存方式代码示例2
这种方法用for循环加readlines实现,可以模糊匹配让后整行替换
```python
f_name = "my.txt"
old_str = "晴川历历汉阳树,芳草萋萋鹦鹉洲"
new_str = "哈哈哈"
f = open(f_name,'r+',encoding="utf-8")
data=f
i=0
for line in data.readlines():
if i ==0:
f.seek(0)
f.truncate()
i+=1
if old_srt in line:
line=new_srt
f.write(line)
print(f.tell())
f.close()
```
- 基础部分
- 基础知识
- 变量
- 数据类型
- 数字与布尔详解
- 列表详解list
- 字符串详解str
- 元组详解tup
- 字典详解dict
- 集合详解set
- 运算符
- 流程控制与循环
- 字符编码
- 编的小程序
- 三级菜单
- 斐波那契数列
- 汉诺塔
- 文件操作
- 函数相关
- 函数基础知识
- 函数进阶知识
- lambda与map-filter-reduce
- 装饰器知识
- 生成器和迭代器
- 琢磨的小技巧
- 通过operator函数将字符串转换回运算符
- 目录规范
- 异常处理
- 常用模块
- 模块和包相关概念
- 绝对导入&相对导入
- pip使用第三方源
- time&datetime模块
- random随机数模块
- os 系统交互模块
- sys系统模块
- shutil复制&打包模块
- json&pickle&shelve模块
- xml序列化模块
- configparser配置模块
- hashlib哈希模块
- subprocess命令模块
- 日志logging模块基础
- 日志logging模块进阶
- 日志重复输出问题
- re正则表达式模块
- struct字节处理模块
- abc抽象类与多态模块
- requests与urllib网络访问模块
- 参数控制模块1-optparse-过时
- 参数控制模块2-argparse
- pymysql数据库模块
- requests网络请求模块
- 面向对象
- 面向对象相关概念
- 类与对象基础操作
- 继承-派生和组合
- 抽象类与接口
- 多态与鸭子类型
- 封装-隐藏与扩展性
- 绑定方法与非绑定方法
- 反射-字符串映射属性
- 类相关内置方法
- 元类自定义及单例模式
- 面向对象的软件开发
- 网络-并发编程
- 网络编程SOCKET
- socket简介和入门
- socket代码实例
- 粘包及粘包解决办法
- 基于UDP协议的socket
- 文件传输程序实战
- socketserver并发模块
- 多进程multiprocessing模块
- 进程理论知识
- 多进程与守护进程
- 锁-信号量-事件
- 队列与生产消费模型
- 进程池Pool
- 多线程threading模块
- 进程理论和GIL锁
- 死锁与递归锁
- 多线程与守护线程
- 定时器-条件-队列
- 线程池与进程池(新方法)
- 协程与IO模型
- 协程理论知识
- gevent与greenlet模块
- 5种网络IO模型
- 非阻塞与多路复用IO实现
- 带着目标学python
- Pycharm基本使用
- 爬虫
- 案例-爬mzitu美女
- 案例-爬小说
- beautifulsoup解析模块
- etree中的xpath解析模块
- 反爬对抗-普通验证码
- 反爬对抗-session登录
- 反爬对抗-代理池
- 爬虫技巧-线程池
- 爬虫对抗-图片懒加载
- selenium浏览器模拟