web-dev-qa-db-fra.com

Comment ouvrir (lecture-écriture) ou créer un fichier avec troncature autorisée?

Je veux:

  • ouvrir un fichier en mode lecture-écriture s'il existe;
  • créez-le s'il n'existe pas;
  • être en mesure de le tronquer à tout moment, n'importe où.

[~ # ~] modifier [~ # ~] : avec tronquer, je veux dire écrire jusqu'à une position et jeter la partie restante du fichier, si elle est présente

Tout cela atomiquement (avec un seul appel open() ou en simulant un seul appel open())

Aucune modalité ouverte unique ne semble s'appliquer:

  • r: ne fonctionne évidemment pas;
  • r +: échoue si le fichier n'existe pas;
  • w: recrée le fichier s'il existe;
  • w +: recrée le fichier s'il existe;
  • a: ne sait pas lire;
  • a +: ne peut pas tronquer.

Certaines combinaisons que j'ai essayées (rw, rw +, r + w, etc.) ne semblent pas fonctionner non plus. C'est possible?

Certains doc de Ruby (s'applique à python aussi)):

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

Selon OpenGroup :

O_TRUNC

Si le fichier existe et est un fichier normal et que le fichier est ouvert avec succès O_RDWR ou O_WRONLY, sa longueur est tronquée à 0 et le mode et le propriétaire sont inchangés. Il n'aura aucun effet sur FIFO fichiers spéciaux ou fichiers de terminal. Son effet sur les autres types de fichiers dépend de l'implémentation. Le résultat de l'utilisation de O_TRUNC avec O_RDONLY n'est pas défini.

Ainsi, O_TRUNC est probablement passé lors de l'ouverture d'un fichier avec "w" ou "w +". Cela donne à "troncature" un sens différent, pas ce que je veux.

Avec python la solution semble ouvrir le fichier à des E/S de bas niveau avec la fonction os.open().

La fonction python suivante:

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)

a le comportement que je voulais. Vous pouvez l'utiliser comme ceci (c'est en fait mon cas d'utilisation):

# 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

Eh bien, il n'y a que ces modes, et tous ont les "défauts" que vous avez énumérés.

Votre seule option consiste à encapsuler open(). Pourquoi pas quelque chose comme ça? (Python)

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

il se comporte comme open, vous pouvez même le relier à open () si vous le souhaitez vraiment.

toutes les fonctionnalités d'Open sont préservées, vous pouvez même faire:

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

Vous pouvez bien sûr créer un gestionnaire de contexte qui ouvre le fichier en mode +, le lit en mémoire et intercepte les écritures afin que vous gériez la troncature en créant comme par magie un fichier temporaire en mode w et en renommant ce fichier temporaire en votre fichier d'origine lorsque vous le fermez, mais ce serait exagéré, je suppose.

14
ch3ka

Vous pouvez lire, écrire et tronquer avec "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

Je ne connais aucune manière élégante de faire exactement cela en Ruby. Ma solution serait probablement de créer un fichier temporaire, d'y écrire du contenu, puis de le renommer avec le nom de fichier que je voulais vraiment. Cela remplacerait le fichier précédent s'il existe, ou créerait le fichier s'il n'existait pas. Quelque chose comme ça:

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)

Le renommage augmentera SystemCallError s'il échoue pour une raison quelconque.

0
Paul Simpson