[Liao Xuefeng] python_IO プログラミング: StringIO と BytesIO、ファイルとディレクトリの操作、シリアル化

IOプログラミング

IOとはコンピュータにおけるInput/Output、つまり入力出力を指します。プログラムとランタイム データはメモリ内に存在し、CPU の超高速コンピューティング コアによって実行されるため、データ交換が関係する場所 (通常はディスク、ネットワークなど) では IO インターフェイスが必要になります。

IO プログラミングでは、ストリームは非常に重要な概念です。ストリームは水道管と考えることができ、データはパイプ内の水ですが、一方向にしか流れることができません。入力ストリームとは外部(ディスク、ネットワーク)からメモリにデータが流れることを指し、出力ストリームとはメモリから外部にデータが流れることを指します。Web を閲覧するには、データの送受信の両方を可能にするために、ブラウザと Sina サーバーの間に少なくとも 2 つの水道管を確立する必要があります。

CPU とメモリの速度は周辺機器の速度よりもはるかに高いためIO プログラミングには重大な速度不一致の問題が発生します。たとえば、100M のデータをディスクに書き込む場合、CPU が 100M のデータを出力するのにかかる時間は 0.01 秒だけですが、ディスクが 100M のデータを受信するのに 10 秒かかる場合があります。次の 2 つの方法があります。

1 つ目は、CPU が待機することです。つまり、プログラムは後続のコードの実行を一時停止し、10 秒後に 100M のデータがディスクに書き込まれるのを待ってから実行を継続します。このモードは同期 IOと呼ばれます

もう 1 つの方法は、CPU が待機せず、ディスクに「いつも書き込みが遅いから心配しないで、他のことを続けます。」と伝えるだけで、後続のコードはすぐに実行されます。このモードはと呼ばれます。非同期IO

同期と非同期の違いは、IO の実行結果を待つかどうかですそれは、マクドナルドに行って食べ物を注文するときに「ハンバーガー」と言うと、ウェイターが「申し訳ありませんが、ハンバーガーは作りたてで調理する必要があるので、5分待つ必要があります」と言い、それでレジカウンターの前に立つようなものです。 5 分間待って、ハンバーガーを買って、ショッピングモールに行きます。これは同期 IO です。

あなたが「ハンバーガーを食べて」と言うと、ウェイターはハンバーガーができるまで 5 分待つ必要があると言います。あなたは先にモールに行って大丈夫です。準備ができたら、すぐに他のことをできるように通知します (行ってください)モールへ)非同期IOです。

明らかに、非同期 IO を使用してプログラムを作成するパフォーマンスは同期 IO よりもはるかに高くなりますが、非同期 IO の欠点はプログラミング モデルが複雑であることです。考えてみてください。「ハンバーガーの準備ができました」といつ通知するかを知る必要があり、通知方法もさまざまです。ウェイターがあなたを見つけに来る場合、これはコールバック モードです。ウェイターがあなたに通知するためにテキスト メッセージを送信する場合、あなたは電話をチェックし続ける必要があります、これがポーリング モードです。つまり、非同期 IO の複雑さは同期 IO の複雑さよりもはるかに高くなります。

IO を操作する機能はオペレーティング システムによって提供されます。すべてのプログラミング言語は、使いやすいようにオペレーティング システムによって提供される低レベルの C インターフェイスをカプセル化しており、 Python も例外ではありません。この章の IO プログラミングはすべて同期モードで行われます。

StringIO と BytesIO

文字列IO

多くの場合、データの読み取りと書き込みは必ずしもファイルではなく、メモリ内での読み取りと書き込みも可能です。

StringIO は、その名前が示すように、メモリ内の str の読み取りと書き込みを行います

str を StringIO に書き込むには、まず StringIO を作成してからそれをファイルのように書き込む必要があります

>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())  # 用于获取写入后的 str
hello world!

getvalue()このメソッドは、書き込み後に str を取得するために使用されます。

StringIO を読み取るには、StringIO を str で初期化しそれを file のように読み取ることができます。

>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
...     s = f.readline()
...     if s == '':
...         break
...     print(s.strip())
...
Hello!
Hi!
Goodbye!

バイトIO

StringIO は str のみを操作できるため、バイナリデータを操作したい場合は BytesIO を使用する必要があります。

BytesIO は、メモリ内のバイトの読み取りと書き込みを実装します。BytesIO を作成し、いくつかのバイトを書き込みます。

>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())  # 用于获取写入后的bytes
b'\xe4\xb8\xad\xe6\x96\x87'

書き込まれるのは str ではなく、UTF-8 でエンコードされたバイトであることに注意してください。

StringIO と同様に、BytesIO は 1 バイトで初期化し、ファイルのように読み取ることができます。

>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'

まとめ

StringIO と BytesIO は、メモリ内の str と bytes を操作するメソッドであり、ファイルの読み取りと書き込みに一貫したインターフェイスを備えています。


ファイルとディレクトリを操作する

ファイルやディレクトリを操作したい場合は、OSが提供するさまざまなコマンドをコマンドラインに入力して完了します。たとえばdircp注文を待ちます。

Python プログラムでこれらのディレクトリとファイルに対して操作を実行したい場合はどうすればよいでしょうか? 実際、オペレーティング システムが提供するコマンドは、オペレーティング システムが提供するインターフェイス関数を呼び出すだけですが、Python の組み込みosモジュールは、オペレーティング システムが提供するインターフェイス関数を直接呼び出すこともできます。

osPython 対話型コマンド ラインを開いて、モジュールの基本機能の使用方法を見てみましょう。

>>> import os
>>> os.name
'nt'

「はい」の場合はposix、システムが であることを示しLinuxはい」の場合は、それがシステムであることを示します。UnixMac OS XntWindows

uname()詳細なシステム情報を取得するには、この関数を呼び出します。しかし!これらの機能は Windows では提供されていないことに注意してくださいuname()。つまり、osモジュールの一部の機能はオペレーティング システムに関連しています。(泣く!!!)

環境変数

オペレーティング システムで定義されたすべての環境変数はos.environこの変数に保存され、直接表示できます。

>>> os.environ
[Squeezed text(147 lines)]  # 我的显示结果

環境変数の値を取得するには、次のように呼び出しますos.environ.get('key')

>>> os.environ.get('PATH')
[Squeezed text(50 lines)]  # 我的显示结果
>>> os.environ.get('x', 'default')
'default'

ファイルとディレクトリを操作する

ファイルやディレクトリを操作する関数の中にはos、モジュール内に配置されるものとos.pathモジュール内に配置されるものがあるので注意が必要です。ディレクトリの表示、作成、削除は次のように呼び出すことができます。

# 查看当前目录的绝对路径:
>>> os.path.abspath('.')  
'C:\\Program Files\\Python39'

# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('E:/', 'testdir')
'E:/testdir'

# 然后创建一个目录:
>>> os.mkdir('E:/testdir')

# 删掉一个目录:
>>> os.rmdir('E:/testdir')

パスを分割する場合は、文字列を直接分割するのではなく、os.path.split()関数を使用します。この方法でパスを 2 つの部分に分割できます。後者の部分は常に最終レベルのディレクトリまたはファイル名になります。

>>> os.path.split('E:\\submit\\submit\\README.md')
('E:\\submit\\submit', 'README.md')

os.path.splitext()ファイル拡張子を直接取得できるため、多くの場合に非常に便利です。

>>> os.path.splitext('E:\\submit\\submit\\README.md')
('E:\\submit\\submit\\README', '.md')

パスを結合および分割するこれらの関数は、ディレクトリやファイルが実際に存在する必要はなく、文字列に対してのみ動作します。

ファイル操作には以下の関数を使用します。test.txt現在のディレクトリにファイルがあると仮定します。

# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')

しかし、ファイルをコピーする機能はosモジュールに存在しません。その理由は、ファイルのコピーがオペレーティング システムによって提供されるシステム コールではないためです理論的には、前のセクションでファイルの読み取りと書き込みを行うことでファイルのコピーを完了できますが、さらに多くのコードを記述する必要があります。

幸いなことに、shutilmodule によって提供される関数はcopyfile()shutilモジュール内に多くの実用的な関数もあり、osモジュールの補足とみなすことができます。

最後に、Python の機能を使用してファイルをフィルタリングする方法を見てみましょう。たとえば、現在のディレクトリ内のすべてのディレクトリを一覧表示したい場合、必要なコードは 1 行だけです。

>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['DLLs', 'Doc', 'include', 'Lib', 'libs', 'Scripts', 'share', 'tcl', 'Tools']

すべての.pyファイルをリストするには、コードを 1 行記述するだけです。

>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
[]

まとめ

Pythonモジュールは、オペレーティング システムのディレクトリおよびファイル操作をカプセル化します。これらの関数の一部はモジュール内にあり、一部はモジュール内にあるosことに注意してください。osos.path

練習する

1.osモジュールを使用して、dir -l出力を実現できるプログラムを作成します。

>>> [x for x in os.listdir('E:/submit/submit/')]
['.DS_Store', '.idea', 'config1.json', 'config2.json', 'config3.json', 'data.py', 'demo', 'inference.py', 'model.py', 'README.html', 'README.md', 'results', 'run.sh.rjejmartr', 'train.py', '__pycache__']

2.現在のディレクトリおよび現在のディレクトリのすべてのサブディレクトリで、指定した文字列をファイル名に含むファイルを検索し、相対パスを出力するプログラムを作成します

import os
def search_file(path, str):
    # 首先找到当前目录下的所有文件
    # os.listdir(path) 是当前这个path路径下的所有文件的列表,包括子目录、子目录下的目录及文件
    for file in os.listdir(path):
        this_path = os.path.join(path, file)
        if os.path.isfile(this_path):  # 判断这个路径对应的是目录还是文件,是文件就走下去
            if str in file:
                print(this_path)
        else:
            search_file(this_path, str)


if __name__ == "__main__":
    search_file("F:/7788/", "王")

連載

プログラムの実行中、すべての変数はメモリ内にあります。たとえば、辞書を定義します。

>>> d = dict(name='Bob', age=20, score=88)
>>> d
{
    
    'name': 'Bob', 'age': 20, 'score': 88}

name変数は、に変更するなどいつでも変更できますが、'Bill'プログラムが終了すると、変数によって占有されていたすべてのメモリがオペレーティング システムによって再利用されます。変更が'Bill'ディスクに保存されていない場合、次回プログラムを再実行するときに変数が再度初期化されます'Bob'

変数をメモリから保存可能または転送可能に変更するプロセスをPython ではピクルと呼びます他の言語ではシリアル化、マーシャリング、フラット化などとも呼ばれます。これらはすべて同じ意味です。

シリアル化後、シリアル化されたコンテンツをディスクに書き込んだり、ネットワーク経由で他のマシンに送信したりできます。

また、変数の内容をシリアル化されたオブジェクトからメモリに再読み取ることは、逆シリアル化、つまりunpickle と呼ばれます。

Python はpickleシリアル化を実装するためのモジュールを提供します。

まず、オブジェクトをシリアル化してファイルに書き込もうとします。

>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x04\x95$\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03Bob\x94\x8c\x03age\x94K\x14\x8c\x05score\x94KXu.'

pickle.dumps()メソッドは任意のオブジェクトを 1 つにシリアル化し、ファイルに書き込むbytesことができます。bytesまたは、別のメソッドを使用してpickle.dump()オブジェクトを直接シリアル化し、ファイルのようなオブジェクトに書き込みます。

>>> f = open(r'E:\csdn\practice\dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

書かれたファイルを見てください。これは、 Python によって保存されたオブジェクトの内部情報dump.txtである、乱雑な内容の束です

オブジェクトをディスクからメモリに読み取る場合は、まずコンテンツを one に読み取りbytes、次にpickle.loads()メソッドを使用してオブジェクトを逆シリアル化するか、 メソッドを直接使用してpickle.load()one からオブジェクトをfile-like Object直接逆シリアル化することができます。別の Python コマンド ラインを開いて、保存したオブジェクトを逆シリアル化します。

# loads()将bytes序列化
>>> pickle_b = b'\x80\x04\x95$\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03Bob\x94\x8c\x03age\x94K\x14\x8c\x05score\x94KXu.'
>>> pickle.loads(pickle_b)
{
    
    'name': 'Bob', 'age': 20, 'score': 88}

# load()从一个file-like Object中直接反序列化出对象
>>> f = open(r'E:\csdn\practice\dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{
    
    'name': 'Bob', 'age': 20, 'score': 88}

変数の内容が戻ってきました。

もちろん、この変数と元の変数はまったく関連のないオブジェクトであり、内容が同じであるだけです。

Pickle の問題は、他のすべてのプログラミング言語に特有のシリアル化の問題と同様、Python でのみ使用できること、および Python の異なるバージョンには相互互換性がない可能性があるため、重要でないデータのみを保存できることです。 Pickle は正常に使用できません。デシリアライズも問題ありません。

JSON

異なるプログラミング言語間でオブジェクトを転送したい場合は、オブジェクトを XML などの標準形式にシリアル化する必要がありますが、JSON は文字列として表現され、すべての言語で使用できるため、より良い方法はJSON にシリアル化することです。読み取り値は、ディスクに簡単に保存したり、ネットワーク経由で送信したりすることもできます。JSON は標準形式であり、XML よりも高速であるだけでなく、Web ページで直接読み取ることもできるため、非常に便利です。

JSON で表されるオブジェクトは標準的な JavaScript 言語オブジェクトであり、JSON と Python の組み込みデータ型の対応は次のとおりです。

JSONタイプ Python の種類
{} 辞書
[] リスト
"弦" str
1234.56 整数または浮動小数点
真/偽 真/偽
ヌル なし

Python の組み込みjsonモジュールは、Python オブジェクトを JSON 形式に完全に変換します。まず、Python オブジェクトを JSON に変換する方法を見てみましょう。

>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"name": "Bob", "age": 20, "score": 88}'  # 返回的是str

dumps()このメソッドは 1 を返しstr、コンテンツは標準の JSON です。同様に、dump()メソッドは JSON を に直接書き込むことができますfile-like Object

>>> f = open(r'E:\csdn\practice\json_dump.txt', 'w')
>>> json.dump(d, f)  # 写入的是字符串,且不含引号
>>> f.close()

JSON を Python オブジェクトに逆シリアル化するには、loads()または対応するload()メソッドを使用します。前者は JSON 文字列を逆シリアル化し、後者はそこfile-like Objectから逆シリアル化します。

# loads()将json的字符串反序列化
>>> json_str = '{"name": "Bob", "age": 20, "score": 88}'
>>> json.loads(json_str)
{
    
    'name': 'Bob', 'age': 20, 'score': 88}

# load从file-like Object中读取字符串并反序列化
>>> f = open(r'E:\csdn\practice\json_dump.txt', 'r')
>>> d = json.load(f)
>>> f.close()
>>> d
{
    
    'name': 'Bob', 'age': 20, 'score': 88}

strJSON 標準では JSON エンコーディングが UTF-8 であると指定されているため、 Python 文字列と JSON 文字列の間で常に正しく変換できます。

JSON 上級者向け

Pythondictオブジェクトは JSON に直接シリアル化できます{}が、多くの場合、クラスclassの定義などの表現オブジェクトを使用しStudentてからシリアル化することを好みます。

デフォルトでは、メソッドはインスタンスを JSON オブジェクトにdumps()変換する方法を認識しませんオプションのパラメーターは、任意のオブジェクトを JSON にシーケンスできるオブジェクトに変換することです。特別に変換関数を作成し、その関数を次のように渡すだけです。Student{}defaultStudent

import json

class Student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score

# 为Student专门写一个转换函数
def student2dict(std):
    return {
    
    
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

s = Student('Bob', 20, 88)
print(json.dumps(s, default=student2dict))
# 可选参数default就是把任意一个对象变成一个可序列为JSON的对象

# 结果:{"name": "Bob", "age": 20, "score": 88}

ただし、Teacher次にクラスのインスタンスに遭遇した場合、そのインスタンスは JSON にシリアル化されません。怠けて、任意のclassインスタンスを次のように変えることもできますdict

print(json.dumps(s, default=lambda obj: obj.__dict__))
# {"name": "Bob", "age": 20, "score": 88}

通常、インスタンスにはインスタンス変数を格納するために使用されるプロパティ ( one ) がclassあるためです定義されたクラスなど、いくつかの例外があります__dict__dict__slots__

同様に、JSON をStudentオブジェクト インスタンスに逆シリアル化する場合、loads()メソッドは最初にdictオブジェクトを変換し、次にobject_hook渡した関数がdictそれをStudentインスタンスに変換します。

def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

実行結果は次のとおりです。

json_str = '{"name": "Bob", "age": 20, "score": 88}'
print(json.loads(json_str, object_hook=dict2student))
# 结果:<__main__.Student object at 0x000001D56BD649B0>

出力されるのは、逆シリアル化されたStudentインスタンス オブジェクトです。

練習する

JSON を中国語用にシリアル化する場合、結果に対するこのパラメーターの影響を観察するためのjson.dumps()パラメーターが提供されます。ensure_ascii

import json
obj = dict(name='小明', age=20)
s = json.dumps(obj, ensure_ascii=True)
print(s)
# 结果:{"name": "\u5c0f\u660e", "age": 20}

まとめ

Python 言語固有のシリアル化モジュールは ですpickle。ただし、シリアル化をより一般的にし、Web 標準に準拠させたい場合は、jsonモジュールを使用できます。

jsonモジュールdumps()loads()関数は、明確に定義されたインターフェイスの良い例です。これを使用するときは、必須パラメーターを 1 つ渡すだけで済みます。ただし、デフォルトのシリアル化または逆シリアル化メカニズムが要件を満たさない場合は、より多くのパラメーターを渡してシリアル化または逆シリアル化ルールをカスタマイズできます。これにより、インターフェイスがシンプルで使いやすくなるだけでなく、十分なスケーラビリティと柔軟性も実現されます。

参考リンク:IO Programming - Liao Xuefeng公式サイト(liaoxuefeng.com)

おすすめ

転載: blog.csdn.net/qq_45670134/article/details/127203316