[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瀏覽器模擬