web-dev-qa-db-ja.com

切り捨てが許可されたファイルを開く(読み取り/書き込み)または作成する方法

したい:

  • ファイルが存在する場合は、読み取り/書き込みモードで開きます。
  • 存在しない場合は作成します。
  • いつでもどこでもそれを切り捨てることができます。

[〜#〜] edit [〜#〜]:truncateでは、ある位置まで書き込み、ファイルの残りの部分があればそれを破棄します

これはすべてアトミックに(単一のopen()呼び出しを使用するか、単一のopen()呼び出しをシミュレートして)

単一のオープンモダリティは適用されないようです:

  • r:明らかに機能しません。
  • r +:ファイルが存在しない場合は失敗します。
  • w:ファイルが存在する場合は再作成します。
  • w +:ファイルが存在する場合は再作成します。
  • a:読めません。
  • a +:切り捨てできません。

私が試したいくつかの組み合わせ(rw、rw +、r + wなど)も機能しないようです。出来ますか?

一部の doc from Ruby(pythonも適用):

r
Read-only mode. The file pointer is placed at the beginning of the file.
This is the default mode.

r+
Read-write mode. The file pointer will be at the beginning of the file.

w
Write-only mode. Overwrites the file if the file exists. If the file
does not exist, creates a new file for writing.

w+
Read-write mode. Overwrites the existing file if the file exists. If the
file does not exist, creates a new file for reading and writing.

a
Write-only mode. The file pointer is at the end of the file if the file
exists. That is, the file is in the append mode. If the file does not exist,
it creates a new file for writing.

a+
Read and write mode. The file pointer is at the end of the file if the file
exists. The file opens in the append mode. If the file does not exist, it
creates a new file for reading and writing.
36
ceztko

OpenGroup によると:

O_TRUNC

ファイルが存在し、通常のファイルであり、ファイルがO_RDWRまたはO_WRONLYで正常に開かれた場合、その長さは0に切り捨てられ、モードと所有者は変更されません。 FIFO特殊ファイルまたは端末デバイスファイルには影響しません。他のファイルタイプへの影響は実装依存です。O_TRUNCをO_RDONLYで使用した結果は未定義です。

そのため、「w」または「w +」でファイルを開くと、おそらくO_TRUNCが渡されます。これは「切り捨て」に別の意味を与え、私が望むものではありません。

pythonを使用すると、ソリューションはos.open()関数を使用して低レベルI/Oでファイルを開くようです。

次のpython関数:

def touchopen(filename, *args, **kwargs):
    # Open the file in R/W and create if it doesn't exist. *Don't* pass O_TRUNC
    fd = os.open(filename, os.O_RDWR | os.O_CREAT)

    # Encapsulate the low-level file descriptor in a python file object
    return os.fdopen(fd, *args, **kwargs)

私が望んでいた動作をしています。次のように使用できます(実際、私の使用例です)。

# Open an existing file or create if it doesn't exist
with touchopen("./tool.run", "r+") as doing_fd:

    # Acquire a non-blocking exclusive lock
    fcntl.lockf(doing_fd, fcntl.LOCK_EX)

    # Read a previous value if present
    previous_value = doing_fd.read()
    print previous_value 

    # Write the new value and truncate
    doing_fd.seek(0)
    doing_fd.write("new value")
    doing_fd.truncate()
30
ceztko

さて、これらのモードのみがあり、それらのすべてには、リストした「欠陥」があります。

唯一のオプションは、open()をラップすることです。このようなものはなぜですか? (Python)

def touchopen(filename, *args, **kwargs):
    open(filename, "a").close() # "touch" file
    return open(filename, *args, **kwargs)

openと同じように動作し、本当に望むならopen()に再バインドすることもできます。

openの機能はすべて保持されますが、次のこともできます。

with touchopen("testfile", "r+") as testfile:
    do_stuff()

もちろん、ファイルを+モードで開き、メモリに読み込み、書き込みをインターセプトするコンテキストマネージャーを作成して、wモードで一時ファイルを魔法のように作成して切り捨てを処理し、閉じたときにその一時ファイルの名前を元のファイルに変更することができますしかし、それはやり過ぎだと思います。

14
ch3ka

"a +"(Ruby)で読み取り、書き込み、切り捨てができます:

File.open("test.txt", "a+") do |f|
  f.print "abc\ndefgh" 
  f.rewind
  p f.read 
  f.truncate(5) 
end
puts File.size("test.txt") #=> 5
2
steenslag

Rubyでこれを正確に行うエレガントな方法は知りません。私の解決策は、おそらく一時ファイルを作成し、そのファイルに内容を書き込んでから、本当に必要なファイル名に名前を変更することでしょう。これにより、前のファイルが存在する場合は上書きされ、存在しない場合はファイルが作成されます。このようなもの:

orig_filename = './whatever_file.log'
temp_filename = './.tempfile'
temp_file = File.new(temp_filename, 'w')

// Write contents to file

temp_file.close
File.rename(temp_filename, orig_filename)

何らかの理由で失敗した場合、名前の変更はSystemCallErrorを発生させます。

0
Paul Simpson