室內(nèi)設(shè)計軟件手機版semseo是什么意思
上一篇中我們介紹了文件的基本讀寫操作,但是很多時候數(shù)據(jù)的讀寫并不一定都是在文件中,我們也可以在內(nèi)存中讀寫數(shù)據(jù),因此引出我們今天的主要內(nèi)容,即 StringIO 和 BytesIO,讓你學(xué)會在內(nèi)存中進行數(shù)據(jù)的基本讀寫操作。
1 前言-內(nèi)存與硬盤
在正式介紹 StringIO 和 BytesIO 之前,我們先來了解一下內(nèi)存和硬盤的差異,以便更好的理解硬盤中文件的基本操作與 StringIO 和 BytesIO 對數(shù)據(jù)的基本操作兩者之間存在的意義。
內(nèi)存與硬盤的差異:
差異點 | 內(nèi)存 | 硬盤 |
---|---|---|
形狀 | 長條形,所以有內(nèi)存條之稱 | 四四方方的,內(nèi)含盤片 |
容量(以 PC 機為例) | 4G | 1T |
功能 | 存儲任務(wù)管理器的進程 | 存儲文檔、軟件等數(shù)據(jù) |
運行速度 | 快 | 慢 |
特點 | 存放 CPU 運算的數(shù)據(jù),一旦斷電數(shù)據(jù)就會消失 | 可以永久存儲數(shù)據(jù) |
通俗點來講,我們電腦里的 C 盤、D盤等都是硬盤,電腦關(guān)機后再次開機這些盤符里面的數(shù)據(jù)依然還在。但是我們電腦任務(wù)管理器里面跑的進程的數(shù)據(jù)都是存儲在內(nèi)存中的,電腦關(guān)機后進程里面的數(shù)據(jù)就不復(fù)存在了。
正是由于硬盤的讀取數(shù)據(jù)比較慢,CPU 如果在運行程序的時候所有數(shù)據(jù)都直接從硬盤中讀寫,那么電腦的運行速度將會大打折扣。因此 CPU 會將運行軟件時要用到的數(shù)據(jù)一次性從硬盤中調(diào)到運行速度很快的內(nèi)存中,然后 CPU 再與內(nèi)存進行數(shù)據(jù)交換。一個很明顯的現(xiàn)象就是我們在打開一個軟件時會有一段時間延遲,但是打開之后軟件的運行速度就很快了。
好了,現(xiàn)在我們應(yīng)該對在內(nèi)存與硬盤上讀取數(shù)據(jù)大概有一個了解,開始進入我們的正題。
2 StringIO 和 BytesIO
StringIO 和 BytesIO 的作用簡單來說,就是在內(nèi)存中虛擬一個文件的感覺,這個虛擬出來的文件操作方式與上一篇介紹的在硬盤中文件的基本操作類似。在 Python3 中,這兩“兄弟”現(xiàn)在已經(jīng)歸入 IO 模塊。
2.1 StringIO
要把 str 字符串寫入內(nèi)存中,我們需要創(chuàng)建一個 StringIO 對象,然后像文件一樣對讀取內(nèi)容。其中 StringIO 中多了一個 getvalue() 方法,目的是用于獲取寫入后的 str。
示例 1:
# 定義一個 StringIO 對象,寫入并讀取其在內(nèi)存中的內(nèi)容
from io import StringIO
f = StringIO()
f.write('Python-100')
str = f.getvalue()
print('寫入內(nèi)存中的字符串為:%s' %str)
f.write('\n') # 追加寫入內(nèi)容
f.write('堅持100天')
str = f.getvalue() # getvalue() 可以讀取到 StringIO 中的所有內(nèi)容
print('寫入內(nèi)存中的字符串為:\n%s' %str)
f.close() # 釋放內(nèi)存中的數(shù)據(jù),后續(xù)不可再對該 StringIO 進行內(nèi)容的讀寫操作
# 輸出結(jié)果
# 寫入內(nèi)存中的字符串為:
# Python-100
# 寫入內(nèi)存中的字符串為:
# Python-100
# 堅持100天
示例 2:???????
# 當然也可以用 read()、readline() 等來讀取 StringIO 中寫入的字符串
from io import StringIO
str = 'Python-100' + '\n' + '堅持100天'
f = StringIO(str)
currentStr = f.read()
print('寫入內(nèi)存中的字符串為:\n%s' %currentStr)
f.close()
示例 3:???????
# 考慮一個場景,比如你需要對爬蟲爬取到的數(shù)據(jù)進行操作,但是你不想把數(shù)據(jù)寫入本地的硬盤上,這時候 StringIO 就派上用場了。
from io import StringIO
# 假設(shè)的爬蟲數(shù)據(jù)輸出函數(shù) outputData()
def outputData():
dataOne = '我是 1 號爬蟲數(shù)據(jù)\n'
dataTwo = '我是 2 號爬蟲數(shù)據(jù)\n'
dataThree = '我是 3 號爬蟲數(shù)據(jù)'
data = dataOne + dataTwo + dataThree
return data
# dataStr 為爬蟲數(shù)據(jù)字符串
dataStr = outputData()
# 1. 將 outputData() 函數(shù)返回的內(nèi)容寫入內(nèi)存中
dataIO = StringIO(dataStr)
# 1.1 輸出 StringIO 在內(nèi)存中寫入的數(shù)據(jù)
print('1.1內(nèi)存中寫入的數(shù)據(jù)為:\n%s' %dataIO.getvalue())
# 輸出結(jié)果:
# 1.1內(nèi)存中寫入的數(shù)據(jù)為:
# 我是 1 號爬蟲數(shù)據(jù)
# 我是 2 號爬蟲數(shù)據(jù)
# 我是 3 號爬蟲數(shù)據(jù)
# 1.2 按行輸出寫入的數(shù)據(jù)方式一
print('1.2按行輸出寫入的數(shù)據(jù)方式一:')
for data in dataIO.readlines():
print(data.strip('\n')) # 去掉每行數(shù)據(jù)末尾的換行符
# 輸出結(jié)果:
# 1.2按行輸出寫入的數(shù)據(jù)方式一:
# 我是 1 號爬蟲數(shù)據(jù)
# 我是 2 號爬蟲數(shù)據(jù)
# 我是 3 號爬蟲數(shù)據(jù)
# 1.3 按行輸出寫入的數(shù)據(jù)方式二
# 由于上一步的操作,此時文件指針指向數(shù)據(jù)末尾(32),我們需要將指針指向起始位置
print('由于上一步操作的輸出,此時文件指針位置為:%d' %dataIO.tell())
# 將文件指針指向起始位置,方便下面的演示
dataIO.seek(0)
print('1.3按行輸出寫入的數(shù)據(jù)方式二:')
for data in dataIO:
print(data.strip('\n'))
# 輸出結(jié)果:
# 1.3按行輸出寫入的數(shù)據(jù)方式二:
# 我是 1 號爬蟲數(shù)據(jù)
# 我是 2 號爬蟲數(shù)據(jù)
# 我是 3 號爬蟲數(shù)據(jù)
# 2. 采用 write() 的方法將字符串寫入內(nèi)存
dataWriteIO = StringIO()
dataWriteIO.write(dataStr)
# 2.1 輸出內(nèi)存中寫入的數(shù)據(jù)
print('2.1內(nèi)存中寫入的數(shù)據(jù)為:\n%s' %dataIO.getvalue())
# 輸出結(jié)果:
# 2.1內(nèi)存中寫入的數(shù)據(jù)為:
# 我是 1 號爬蟲數(shù)據(jù)
# 我是 2 號爬蟲數(shù)據(jù)
# 我是 3 號爬蟲數(shù)據(jù)
# 2.2 按行輸出寫入的數(shù)據(jù)方式一
# 由于 write() 寫入字符串后,文件指針會指向?qū)懭雰?nèi)容的結(jié)尾,需要將文件指針指向起始位置,否則未能輸出內(nèi)容
print('2.2按行輸出寫入的數(shù)據(jù)方式一:')
print('輸出內(nèi)容為空!')
for data in dataIO:
print(data.strip('\n'))
print('輸出內(nèi)容為空!')
# 輸出結(jié)果:
# 2.2按行輸出寫入的數(shù)據(jù)方式一:
# 輸出內(nèi)容為空!
# 輸出內(nèi)容為空!
# 2.3 按行輸出寫入的數(shù)據(jù)方式二
# 將指針指向起始位置重新按行輸出
dataIO.seek(0)
print('2.3按行輸出寫入的數(shù)據(jù)方式:')
for data in dataIO.readlines():
print(data.strip('\n'))
# 輸出結(jié)果
# 2.3按行輸出寫入的數(shù)據(jù)方式:
# 我是 1 號爬蟲數(shù)據(jù)
# 我是 2 號爬蟲數(shù)據(jù)
# 我是 3 號爬蟲數(shù)據(jù)
Tips: 根據(jù)這個例子可以看出,當我們使用 StringIO(str) 方法向內(nèi)存寫入數(shù)據(jù)時,文件指針是指向起始位置的,比如示例 3 的 1.2 場景中 readlines() 可以讀取到數(shù)據(jù)。當我們使用 write(str) 方法向內(nèi)存寫入數(shù)據(jù)時,文件指針會指向?qū)懭雰?nèi)容的結(jié)尾,讀取數(shù)據(jù)時需要將指針移動到起始位置,比如示例 3 的 2.3 場景。
2.2 BytesIO
BytesIO,顧名思義,就是將字節(jié)流寫入到內(nèi)存中,其實它的操作方法與 StringIO 一樣,區(qū)別就在于前者寫入字節(jié),后者寫入字符串。這邊就簡單舉個例子演示,不再具體介紹了。
示例:???????
# 定義一個 BytesIO 對象,寫入并讀取其在內(nèi)存中的內(nèi)容
from io import BytesIO
str = 'Python-100' + '\n' + '堅持100天'
f = BytesIO(str.encode('utf-8'))
print('寫入內(nèi)存的字節(jié)為:%s' %f.getvalue())
print('字節(jié)解碼后內(nèi)容為:\n%s' %f.getvalue().decode('utf-8'))
Tips: 根據(jù)示例可知,對于字節(jié)我們需要掌握其正確的編解碼方式,比如有 'utf-8'、'gbk' 等。
3 總結(jié)
本節(jié)給大家介紹了 Python 中 StringIO 和 BytesIO 的基本使用方法,掌握在內(nèi)存中存取數(shù)據(jù)的基本操作,同時介紹了內(nèi)存與硬盤的區(qū)別,讓大家明白在內(nèi)存中存取數(shù)據(jù)的優(yōu)勢,助力您在爬蟲的道路越走越遠。