文件存儲


保存數據有多種方式,其中最簡單、成本最低的就是將數據保存在二進制或文本文件中。這些文件主要包括XML文件、CSV文件、JSON文件等。本章詳細介紹Python API讀寫這些文件

一.操作文件的基本方法

1.1打開文件

open函數用於打開文件,通過該函數的第一個參數指定要打開的文件名(可以是相對路徑,也可以是絕對路徑)
如果open函數成功打開文件,那么該函數會返回一個TextIOWrapper對象,該對象中的方法可用來操作這個被打開的文件。如果要打開的文件不存在,會拋出FileNotFoundError異常。
open函數的第二個參數用於指定文件模式(用一個字符串表示),這里的文件模式是指操作文件的方式,如只讀、寫入、追加等

功能描述
w 寫模式
r 讀模式
x 寫模式,創建一個文件,如果文件已存在,則報錯
a 追加模式
b 二進制模式(可與其他模式結合使用)
+ 讀/寫模式(可與其他模式結合使用)
其中 b 或者 + 可與其他模式結合使用需要說明下:
  • 如 rb 就表示讀取一個二進制文件
  • 如w+ 表示對打開的文件可讀可寫
  • 如 wb+ 則表示對二進制文件可讀可寫,如果模式中不加 b則默認表示文本文件
    補充:a+文件可讀寫,如果文件不存在,會創建一個新的文件,如果文件存在,會將要寫入的內容添加到原文件的最后

1.2讀文件和寫文件

TextIOWrapper對象有如下4個常用的方法
1.write(string):像文件寫入內容,該方法返回寫入文件的字節數
2.read([n]):讀取文件的內容,n是一個整數,表示從文件指針指定的位置開始讀取的n個字節。如果不指定n,該方法就會讀取從當前位置往后的所有的字節。該方法返回讀取的數據
3.seek(n):重新設置文件指針,也就是改變文件的當前位置。使用write方法向文件寫入內容后,需要調用seek(0),才能讀取剛才寫入的內容。這個意思就是寫完馬上要讀的話,就要把指針移到前面去,不然讀空
4.close():關閉文件

# 以寫模式打開test1.txt文件
f = open('./files/test1.txt','w')
# 向test1.txt文件寫入“I love ",運行結果:7
print(f.write('I love '))
# 向test1.txt文件寫入“python",運行結果:6
print(f.write('python'))
# 關閉test1.txt文件
f.close()
# 以讀模式打開test1.txt文件
f = open('./files/test1.txt', 'r')
# 從test1.txt文件中讀取7個字節的數據,運行結果:I love
print(f.read(7))
# 從test1.txt文件的當前位置開始讀取6個字節的數據,運行結果:python
print(f.read(6))
# 關閉test.txt文件
f.close()
try:
    # 如果test2.txt文件不存在,會拋出異常
    f = open('./files/test2.txt','r+')
except Exception as e:
    print(e)
# 用追加可讀寫模式打開test2.txt文件
f = open('./files/test2.txt', 'a+')
# 向test2.txt文件寫入”hello“
print(f.write('hello'))
# 關閉test2.txt文件
f.close()
# 用追加可讀寫模式打開test2.txt文件

f = open('./files/test2.txt', 'a+')
# 讀取test2.txt文件的內容,由於目前文件指針已經在文件的結尾,所以什么都不會讀出來
print(f.read())
# 將文件指針設置到文件開始的位置
# f.seek(0)
# 讀取文件的全部內容,運行結果:hello
print("關閉f.seek(0)指針,看看讀出什么",f.read())
# 關閉test2.txt文件
f.close()
try:
    # 用寫入可讀寫的方式打開test2.txt文件,該文件的內容會清空
    f = open('./files/test2.txt', 'w+')
    # 讀取文件的全部內容,什么都沒讀出來
    print(f.read())
    # 向文件寫入”How are you?“
    f.write('How are you?')
    # 重置文件指針到文件的開始位置
    f.seek(0)
    # 讀取文件的全部內容,運行結果:How are you?
    print(f.read())
finally:
    # 關閉test2.txt文件,建議在finally中關閉文件
    f.close()

1.3讀行和寫行

讀寫一整行是純文本文件最常用的操作,盡管可以使用read和write方法加上行結束符來讀寫文件中的整行,但比較麻煩。因此,要讀寫一行或多行文本,建議使用readline、readlines和writelines方法。注意,並沒有writeline方法,寫一行文本直接使用write方法。
readline方法用於從文件指針當前位置讀取一整行文本,也就是說,遇到行結束符停止讀取文本,但讀取的內容包括了行結束符。
readlines方法從文件指針當前的位置讀取后面所有的數據,並將這些數據按行結束符分隔后,放到列表中返回
writelines方法需要通過參數指定一個字符串類型的列表,該方法會將列表中的每一個元素值作為單獨的一行寫入文件

import os
# 以讀寫模式打開urls.txt文件
f = open('./files/urls.txt','r+')
# 保存當前讀上來的文本
url = ''
while True:
    # 從urls.txt文件讀一行文本
    url = f.readline()
    # 將最后的行結束符去掉
    url = url.rstrip()
    # 當讀上來的是空串,結束循環
    if url == '':
        break;
    else:
        # 輸出讀上來的行文本
        print(url)
print('-----------')
# 將文件指針重新設為0
f.seek(0)
# 讀urls.txt文件中的所有行
print(f.readlines())
# 向urls.txt文件中添加一個新行
f.write('https://jiketiku.com' + os.linesep)
#  關閉文件
f.close()
# 使用'a+'模式再次打開urls.txt文件
f = open('./files/urls.txt','a+')
# 定義一個要寫入urls.txt文件的列表
urlList = ['https://geekori.com' + os.linesep, 'https://www.google.com' + os.linesep]
# 將urlList寫入urls.txt文件
f.writelines(urlList)
# 關閉urls.txt文件
f.close()

二.使用FileInput對象讀取文本

如果需要讀取一個非常大的文件,使用readlines函數會占用太多內存,為了解決這個問題,可以使用for循環和readline方法逐行獲取,也可以使用fileinput模塊中的input函數讀取指定的文件
input方法返回一個FileInput對象,FileInput對象使用的緩存機制,並不會一次性讀取文件的所有內容,所以比readlines函數更節省內存資源
本例使用fileinput.input方法讀取urls.txt文件。

import fileinput
# 使用input方法打開urls.txt文件
fileobj = fileinput.input('./files/urls.txt')
# 輸出fileobj的類型
print(type(fileobj))
# 讀取urls.txt文件第1行
print(fileobj.readline().rstrip())
# 通過for循環輸出urls.txt文件的其他行
for line in fileobj:
    line = line.rstrip()
    # 如果file不等於空串,輸出當前行號和內容
    if line != '':
        print(fileobj.lineno(),':',line)
    else:
        # 輸出當前正在操作的文件名
        print(fileobj.filename())  # 必須在第1行讀取后再調用,否則返回None

三.處理XML格式的數據

3.1讀取與搜索XML文件

讀取XML文件需要導入xml.etree.ElementTree模塊,並通過該模塊的parse函數讀取XML文件

from xml.etree.ElementTree import parse
# 開始分析products.xml文件,files/products.xml是要讀取的XML文件的名字
doc = parse('files/products.xml')
# 通過XPath搜索子節點集合,然后對這個子節點集合進行迭代
for item in doc.iterfind('products/product'):
    # 讀取product節點的id子節點的值
    id = item.findtext('id')
    # 讀取product節點的name子節點的值
    name = item.findtext('name')
    # 讀取product節點的price子節點的值
    price = item.findtext('price')
    # 讀取product節點的uuid屬性的值
    print('uuid','=',item.get('uuid'))
    print('id','=',id)
    print('name', '=',name)
    print('price','=',price)
    print('-------------')

3.2字典轉化為XML字符串

將字典轉化為XML文件需要使用dicttoxml模塊中的dicttoxml函數,在導入dicttoxml模塊之前,需要先使用下面的命令安裝dicttoxml模塊

pip install dicttoxml

本例將字典類型變量轉化為XML字符串,然后使用from xml.dom.minidom import parseString # 解析XML字符串,並用帶縮進格式的形式將XML字符串寫入persons.xml文件

import dicttoxml
from xml.dom.minidom import parseString # 解析XML字符串
import os
# 定義一個字典
d = [20,'names',
     {'name':'Bill','age':30,'salary':2000},
     {'name':'王軍','age':34,'salary':3000},
     {'name':'John','age':25,'salary':2500}]
# 將字典轉換為XML格式(bytes形式)
bxml = dicttoxml.dicttoxml(d, custom_root = 'persons')
# 將bytes形式的XML數據按utf-8編碼格式解碼成XML字符串
xml = bxml.decode('utf-8')
# 輸出XML字符串
print(xml)
# 解析XML字符串
dom = parseString(xml)
# 生成帶縮進格式的XML字符串
prettyxml = dom.toprettyxml(indent = '   ')
# 創建files目錄
os.makedirs('files', exist_ok = True)
# 以只寫和utf-8編碼格式的方式打開persons.xml文件
f = open('files/persons.xml', 'w',encoding='utf-8')
# 將格式化的XML字符串寫入persons.xml文件
f.write(prettyxml)
f.close()

3.3XML字符串轉換為字典

需要導入xmltodict模塊

pip install xmltodict

本例使用xmltodict模塊的parse函數分析這個XML字符串,如果XML格式正確,parse函數會返回與該XML字符串對應的字典對象

import xmltodict
# 打開products.xml文件
f = open('files/products.xml','rt',encoding="utf-8")
# 讀取products.xml文件中的所有內容
xml = f.read()
# 分析XML字符串,並轉化為字典
d = xmltodict.parse(xml)
# 輸出字典內容
print(d)
f.close()
# pprint格式化打印出來
import pprint

pp = pprint.PrettyPrinter(indent=4)
pp.pprint(d)

四.處理JSON格式的數據

JSON格式的數據可以保存數組和對象,JSON數組用一對中括號將數據括起來,JSON對象用一個對大括號將數據括起來。對象中的key和value之間要用冒號(:)分隔,key-value對之間用逗號(,)分隔。注意:key和字符串類型的值要用雙引號括起來,不能使用單引號。

4.1JSON字符串與字典相互轉換

將字典轉換為JSON字符串需要使用json模塊的dumps函數,該函數需要將字典通過參數傳入,然后返回與字典對應的JSON字符串。
將JSON字符粗轉化為字典可以使用下面兩種方法
1.使用json模塊的loads函數,該函數通過參數傳入JSON字符串,然后返回與該JSON字符串對應的字典
2.使用eval函數將JSON格式1字符串當做普通的Python代碼執行,eval函數會直接返回與JSON格式字符串對應的字典。

import json
# 定義一個字典
data = {
    'name' : 'Bill',
    'company' : 'Microsoft',
    'age' : 34
}
# 將字典轉換為JSON字符串
jsonStr = json.dumps(data)
# 輸出jsonStr變量的類型
print('字典轉化為json',type(jsonStr))
# 輸出JSON字符串
print(jsonStr)
# 將JSON字符串轉換為字典
data = json.loads(jsonStr)
print(type(data))
# 輸出字典
print(data)
# 定義一個JSON字符串
s = '''
{
    'name' : 'Bill',
    'company' : 'Microsoft',
    'age' : 34
}
'''
# 使用eval函數將JSON字符串轉換為字典
data = eval(s)
print(type(data))
print(data)
# 輸出字典中的key為company的值
print(data['company'])
# 打開products.json文件
f = open('files/products.json','r',encoding='utf-8')
# 讀取products.json文件中的所有內容
jsonStr = f.read()
# 使用eval函數將JSON字符串轉換為字典
json1 = eval(jsonStr)
# 使用loads函數將JSON字符串轉換為字典
json2 = json.loads(jsonStr)
print(type(json1),json1) # 直接返回列表了
print(type(json2),json2)
print(json2[0]['name'])
f.close()

由於eval函數可以執行任何Python代碼,如果JSON字符串中包含了有害的Python代碼,執行JSON字符串可能會帶來風險;所以盡量使用loads,不要使用eval函數

4.2將JSON字符串轉化為類實例

loads函數不僅可以將JSON字符串轉化為字典,還可以將JSON字符串轉化為類實例。轉換原理是通過loads函數的object_hook關鍵字參數指定一個類或一個回調函數。他們都會由loads函數傳入由JSON字符串轉化成的字典,也就是說,loads函數將JSON字符串轉化為類實例本質上是先將JSON字符串轉化為字典,然后再將字典轉換為對象。區別是指定類時,創建類實例的任務由loads函數完成,而指定回調函數時,創建類實例的任務需要再回調函數中完成,前者更方便,后者更靈活。

import json
class Product:
    # d參數是要傳入的字典
    def __init__(self, d):
        self.__dict__ = d
# 打開product.json文件
f = open('files/product.json','r')
# 從product.json文件中讀取JSON字符串
jsonStr = f.read()
#  通過指定類的方式將JSON字符串轉換為Product對象
my1 = json.loads(jsonStr, object_hook=Product)
# 下面3行代碼輸出Product對象中相應屬性的值
print('name', '=', my1.name)
print('price', '=', my1.price)
print('count', '=', my1.count)
print('-----------')
# 定義用於將字典轉換為Product對象的函數
def json2Product(d):
    return Product(d)
#  通過指定類回調函數的方式將JSON字符串轉換為Product對象
my2 = json.loads(jsonStr, object_hook=json2Product)
# 下面3行代碼輸出Product對象中相應屬性的值
print('name', '=', my2.name)
print('price', '=', my2.price)
print('count', '=', my2.count)
f.close()

4.3將類實例轉化為JSON字符串

dumps函數不僅可以將字典轉換為JSON字符串,還可以將類實例轉化為JSON字符串。dumps函數需要通過default關鍵字指定一個回調函數,在轉化的過程中,dumps函數會像這個回調函數傳入類實例(通過dumps函數的第一個參數傳入),而回調函數的任務是將傳入的對象轉化為字典,然后dumps函數再將字典轉化為JSON字符串。
本例中的product2Dict函數的任務就是將Product類的實例轉化為字典

import json
class Product:
    # 通過類的構造方法初始化3個屬性
    def __init__(self, name,price,count):
        self.name = name
        self.price = price
        self.count = count
# 用於將Product類的實例轉換為字典的函數
def product2Dict(obj):
    return {
        'name': obj.name,
        'price': obj.price,
        'count': obj.count
    }
# 創建Product類的實例
product = Product('特斯拉',1000000,20)
# 將Product類的實例轉換為JSON字符串,ensure_ascii關鍵字參數的值設為True,
# 可以讓返回的JSON字符串正常顯示中文
jsonStr = json.dumps(product, default=product2Dict,ensure_ascii=False)
print(jsonStr)

4.4類實例列表與JSON字符串相互轉換

前面轉換的json對象都是單個對象,弱JSON字符串是一個類實例數組,或一個類實例的列表,也可以互相轉換

import json
class Product:
    def __init__(self, d):
        self.__dict__ = d

f = open('files/products.json','r', encoding='utf-8')
jsonStr = f.read()
# 將JSON字符串轉換為Product對象列表
products = json.loads(jsonStr, object_hook=Product)
# 輸出Product對象列表中所有Product對象的相關屬性值
for product in products:
    print('name', '=', product.name)
    print('price', '=', product.price)
    print('count', '=', product.count)
f.close()
# 定義將Product對象轉換為字典的函數
def product2Dict(product):
    return {
        'name': product.name,
        'price': product.price,
        'count': product.count
        }
# 將Product對象列表轉換為JSON字符串
jsonStr = json.dumps(products, default=product2Dict,ensure_ascii=False)
print(jsonStr)

4.5將JSON字符串轉換為XML字符串

將JSON字符串轉換為XML字符串其實只需要做一下中轉即可,也就是先將JSON字符串轉為字典,然后再使用dicttoxml模塊中的dicttixml函數將字典轉換為XML字符串
本例從products.json文件讀取JSON字符串,並利用loads函數和dicttoxml函數,將JSON字符串轉換為XML字符串

import json
import dicttoxml
f = open('files/products.json','r',encoding='utf-8')
jsonStr = f.read()
# 將JSON字符串轉換為字典
d = json.loads(jsonStr)
print(d)
# 將字典轉換為XML字符串
xmlStr = dicttoxml.dicttoxml(d).decode('utf-8')
print(xmlStr)
f.close()

五.CSV文件存儲

CSV,全稱是Comma-Separated Values,中文可以稱為"逗號分隔值"或“字符分隔值”,CSV文件以純文本形式存儲表格數據。該文件是一個字符序列。CSV文件比Excel文件更加簡潔,Excel文件是電子表格文件,是二進制格式的文件,里面包含了文本、數值、公式,甚至是VBA代碼,而CSV文件中並不包含這些內容,只包含用特定字符分隔的文本,結構清晰簡單,所以在很多場景下使用CSV文件保存數據是比較方便的

5.1寫入CSV文件

本例演示使用csv模塊API將數據寫入CSV文件的完整過程

import csv
with open('files/data0.csv','w',encoding='utf-8') as f:
    # 寫入數據
    writer = csv.writer(f)
    writer.writerow(['產品ID','產品名稱','生產企業','價格'])
    writer.writerow(['0001','iPhone9','Apple',9999])
    writer.writerow(['0002','特斯拉','特斯拉',12345])
    writer.writerow(['0003','榮耀手機','華為',3456])
# 修改字段分隔符
with open('files/data11.csv','w',encoding='utf-8') as f:
    writer = csv.writer(f,delimiter=';')
    writer.writerow(['產品ID','產品名稱','生產企業','價格'])
    writer.writerow(['0001','iPhone9','Apple',9999])
    writer.writerow(['0002','特斯拉','特斯拉',12345])
    writer.writerow(['0003','榮耀手機','華為',3456])

# 一次性寫入多行
with open('files/data22.csv','w',encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['產品ID','產品名稱','生產企業','價格'])
    writer.writerows([['0001','iPhone9','Apple',9999],
                      ['0002', '特斯拉', '特斯拉', 12345],
                      ['0003', '榮耀手機', '華為', 3456]])

# 寫入字典形式的數據
with open('files/data33.csv','w',encoding='utf-8') as f:
    fieldnames = ['產品ID','產品名稱','生產企業','價格']
    writer = csv.DictWriter(f,fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'產品ID': '0001', '產品名稱': 'iPhone9', '生產企業': 'Apple', '價格': 9999})
    writer.writerow({'產品ID': '0002', '產品名稱': '特斯拉', '生產企業': '特斯拉', '價格': 12345})
    writer.writerow({'產品ID': '0003', '產品名稱': '榮耀手機', '生產企業': '華為', '價格': 3456})

# 追加數據
with open('files/data00.csv','a',encoding='utf-8') as f:
    fieldnames = ['產品ID','產品名稱','生產企業','價格']
    writer = csv.DictWriter(f,fieldnames=fieldnames)
    writer.writerow({'產品ID': '0004', '產品名稱': '量子戰衣', '生產企業': '斯塔克工業', '價格': 99999999999})

5.2讀取CSV文件

使用CSV模塊的reader類,可以讀取CSV文件。reader類的實例時可迭代的,所以可以用for循環迭代獲取每一行的數據
如果使用Pandas,通過read_csv函數同樣可以讀取CSV文件的內容
本例演示使用CSV模塊API讀取CSV文件中數據的完整過程

import csv
with open('files/data.csv','r',encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

import pandas as pd
df = pd.read_csv('files/data.csv')
print(df)

其實XML文件、JSON文件、CSV文件本質上都是純文本文件,只是文件的數據組織形式不同。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM