2009-11-28 4 views
15

何らかのXMLファイルや設定ファイルを扱うためのライブラリがあるとします。ライブラリはファイル全体をメモリに読み込み、コンテンツを編集するためのメソッドを提供します。コンテンツの操作が完了したら、writeを呼び出してコンテンツをファイルに保存し直すことができます。問題は安全な方法でこれを行う方法です。安全にファイルに書き込む方法は?

(元のファイルへの書き込みを開始する)既存のファイルを上書きすることは明らかに安全ではありません。 writeメソッドが完了する前に失敗した場合は、半分の書き込みファイルで終了し、データが失われています。

より良いオプションは、どこか、一時ファイルに書き込むことであろう、とwriteメソッドが終了したときに、あなたは、元のファイルに一時ファイルをコピーします。

コピーがどうにか失敗すると、まだ一時ファイルにデータが正しく保存されています。コピーが成功すると、一時ファイルを削除できます。

POSIXシステムでは、アトミック操作であるrenameシステムコールを使用することができますね。しかし、あなたはWindowsシステムでこれをどのようにして最善を尽くすでしょうか?特に、Pythonを使って、このベストをどのように扱いますか?

また、安全にファイルに書き込むための別の方法がありますか?

+0

なぜコピーしますか?なぜ名前を変更しないのですか? –

答えて

14

Pythonのドキュメントを見ると、os.rename()はアトミックな操作であることが明確に分かります。したがって、あなたの場合、一時ファイルにデータを書き込んだ後、元のファイルに名前を変更することは非常に安全です。

もう一つの方法は、次のように仕事ができる:

  • は、元のファイルがabc.xml.tmpを作成し、abc.xmlするリネームabc.xml
  • それに新しいデータを書き込む
  • abc.xmlなりましょう新しいabc.xmlが適切な場所に置かれた後、
  • をabc.xml
abc.xml.bak削除する明博
  • リネームabc.xml.tmp

    あなたはabc.xmlがあることがわかります。tmpファイルに関連する問題があれば復元して、それを元に戻すことができます。

  • +0

    これは、バックアップファイルの削除を追加したS.Lottの回答に似ていますが、これが最善の方法だと思われます。 –

    +0

    ZODB(Zope Object Database)がデータベースファイル(Data.fs)のパッキングを行う方法、つまり古いトランザクションの未使用領域をデータベースファイルから削除する方法で、この実装が実際に見えました。コードは通常のPythonコードです。パッキングは一時ファイルで行われ、その後、上記と同様の手順が実行されます。 ZODBは何年も前からありましたが、WindowsとPOSIXプラットフォームの両方でうまく機能するので、このアプローチはうまくいくはずです。 –

    +3

    Pythonは、名前の変更がアトミックであることを保証することはできません。私が知る限り、OSのシステムコールを呼び出すだけです。 あなたの手続きはうまくいきます。 –

    4

    Win APIでは、オプションのバックアップでも名前が示唆しているように、かなり良い機能が見つかりました。ReplaceFileDeleteFileMoveFileコンボでは常に方法があります。

    一般的にあなたがしたいことは本当に良いです。そして、私はより良い書き込みスキームを考えることができません。

    +3

    MSライブラリAPIを呼び出す適切なPythonコードで説明した方が良いでしょう。 – RedGlyph

    +1

    私はReplaceFileが存在することを認識しませんでした。ドキュメントを読むと、名前を変更するだけではありません。置き換えられたファイルの多くの属性を保持しているため、この目的のために特別に設計されたようです。 –

    3

    単純な解決策です。一時ファイルを作成するにはtempfileを使用し、書き込みが成功した場合はファイルの名前を元の設定ファイルに変更します。

    ファイルをロックするには、portalockerを参照してください。

    +1

    tempfileがターゲットファイルシステム以外のファイルシステムで作成された場合、最終的な名前変更も機能しません。 – tzot

    +0

    名前がアトミックでない限り、データが失われる危険があります。 –

    4

    標準的な解決策はこれです。

    1. 同様の名前で新しいファイルを作成します。 X.ext#などがあります。

    2. このファイルが閉じられている場合(読み込みとチェックサムも)、2つの名前が変更されます。

      • X.ext〜

      • X.ext#(新しいもの)にX.ext(オリジナル)(のみ狂気のため

    3. をX.extしますパラノイド)は、OSシンク関数を呼び出して、ダーティなバッファ書き込みを強制します。

    紛失したり壊れたりすることはありません。名前の変更中に唯一の不具合が発生する可能性があります。しかし、あなたは何かを失ったり、何かを壊したりしていません。元の名前は最終的な名前変更まで直ちに回復可能です。

    +0

    名前を変更しないで(名前を変更してバックアップを作成する)、名前の変更が原子的でない場合は、データが失われる危険性があります。 –

    +1

    *は多くのOSで*アトミックです。しかし、バックアップは操作のアトミック性について手書きで行うよりも重要です。覚えておいてください:クラッシュの確率(Windowsを除く)は非常に小さいです。 –

    11

    正しいPOSIXlyこと、あなたがする必要が保存したい場合:元のファイルの上に一時ファイルに

    1. 書き込み
    2. フラッシュとfsyncファイル(またはfdatasync
    3. 名前の変更

    fsyncを呼び出すとパフォーマンスに予期しない影響を及ぼすことに注意してください。ext3上のLinuxは、結果として他のoに応じてディスクI/Oの整数を秒単位でストールすることがありますI/Oを利用する。

    renameは、ではありません。は、POSIXでのアトミック操作です。少なくとも期待どおりのファイルデータとは関係ありません。しかし、ほとんどのオペレーティングシステムとファイルシステムはこのように動作します。しかし、あなたはExt4と、アトミック性に関するファイルシステムの保証についての非常に大きなLinuxの議論を見逃したようです。リンク先は正確にはわかりませんが、ここにはスタートがあります:ext4 and data loss

    ただし、多くのシステムでは、実際には名前の変更は予想通り安全です。しかし、すべての可能なLinux構成でパフォーマンスと信頼性の両方を得ることは不可能です。

    テンポラリファイルへの書き込みで、テンポラリファイルの名前を変更すると、操作が依存しており、順番に実行されることが予想されます。

    ただし、すべてのファイルシステムではないにしても、大部分がメタデータとデータを分離しています。名前の変更はメタデータのみです。恐ろしいことに聞こえるかもしれませんが、ファイルシステムはメタデータのデータを評価します(例えばHFS +やExt3,4のジャーナリングを取るなど)!その理由は、メタデータが軽いことです。メタデータが壊れていると、ファイルシステム全体が壊れています。ファイルシステムはもちろん、それをそのまま保存してから、その順番でユーザーのデータを保存する必要があります。

    Ext4は最初に出たときに期待通りになりましたが、それを解決するヒューリスティックスが追加されました。問題はで、には失敗しましたが、名前の変更に成功しました。 Ext4は正常に名前を登録するかもしれませんが、その後すぐにクラッシュが発生した場合にはファイルデータの書き込みに失敗します。その結果、長さが0のファイルであり、信号または新データではありません。

    要するに、POSIXはそのような保証をしていません。詳しくはリンク先Ext4の記事を読んでください!

    +0

    POSIXシステムで名前を変更した場合、あなたはその名前空間の名前が変更されたことを保証しません。 "EIO​​"以外の理由でrename()関数が失敗した場合、newで指定されたファイルは影響を受けません。 "それでもまだデータが破損していると思われます。 –

    +0

    問題はaです。名前の変更が成功し、名前の変更だけでは操作全体のアトミック性が保証されないことを意味します。 – u0b34a0f6ae

    +1

    rename()がチェックポイントを持たないということは、確かにアトミックです。http://www.opengroup.org/onlinepubs /009695399/functions/rename.html – geocar

    1

    RedGlyphの提案によれば、Windows APIにアクセスするためにctypesを使用するReplaceFileの実装が追加されています。私は最初これをjaraco.windows.api.filesystemに追加しました。

    ReplaceFile = windll.kernel32.ReplaceFileW 
    ReplaceFile.restype = BOOL 
    ReplaceFile.argtypes = [ 
        LPWSTR, 
        LPWSTR, 
        LPWSTR, 
        DWORD, 
        LPVOID, 
        LPVOID, 
        ] 
    
    REPLACEFILE_WRITE_THROUGH = 0x1 
    REPLACEFILE_IGNORE_MERGE_ERRORS = 0x2 
    REPLACEFILE_IGNORE_ACL_ERRORS = 0x4 
    

    次に、このスクリプトを使用して動作をテストしました。

    from jaraco.windows.api.filesystem import ReplaceFile 
    import os 
    
    open('orig-file', 'w').write('some content') 
    open('replacing-file', 'w').write('new content') 
    ReplaceFile('orig-file', 'replacing-file', 'orig-backup', 0, 0, 0) 
    assert open('orig-file').read() == 'new content' 
    assert open('orig-backup').read() == 'some content' 
    assert not os.path.exists('replacing-file') 
    

    これはWindowsでのみ機能しますが、他の置換ルーチンにはない素晴らしい機能がたくさんあるようです。詳細は、API docsを参照してください。あなたは改行のを書くことができます前に、全体の内容を読み込む必要がある場合は、その後、

    import fileinput 
    for line in fileinput.input(filename,inplace=True, backup='.bak'): 
        # inplace=True causes the original file to be moved to a backup 
        # standard output is redirected to the original file. 
        # backup='.bak' specifies the extension for the backup file. 
    
        # manipulate line 
        newline=process(line) 
        print(newline) 
    

    を:

    0

    あなたはバッキングアップとインプレースがあなたのために書いて処理するためにFileInputクラスモジュールを使用することができます最初にそれを行うことができますし、新しいコンテンツ全体を印刷します

    newcontents=process(contents) 
    for line in fileinput.input(filename,inplace=True, backup='.bak'): 
        print(newcontents) 
        break 
    

    スクリプトが突然終了した場合でも、バックアップは残ります。

    3

    コード化された純粋なPythonがあります。私は、boltons utility libraryboltons.fileutils.atomic_saveのPythonicの解決方法をあえて言います。

    だけpip install boltons、そして:

    from boltons.fileutils import atomic_save 
    
    with atomic_save('/path/to/file.txt') as f: 
        f.write('this will only overwrite if it succeeds!\n') 
    

    実用的なオプション、all well-documentedがたくさんあります。全開示、私はボルトンの著者ですが、この特定の部分は多くのコミュニティの助けを借りて作られました。何かが不明な場合は、drop a noteまでお気軽にお問い合わせください。

    関連する問題