2012-04-27 14 views
31

Quiero:¿Cómo se abre (lectura-escritura) o se crea un archivo con truncamiento permitido?

  • abrir un archivo en modo lectura-escritura si existe;
  • créelo si no existe;
  • ser capaz de truncar en cualquier momento y en cualquier lugar.

EDITAR: con truncado me refiero a escribir hasta una posición y desechar el resto del archivo, si está presente

Todo esto atómicamente (con un solo open() llamada o la simulación de una sola open() llamada)

sin sola modalidad abierta parece aplicarse:

  • r: es evidente que no funciona;
  • r +: falla si el archivo no existe;
  • w: recrear el archivo si existe;
  • w +: vuelve a crear el archivo si existe;
  • a: no se puede leer;
  • a +: no se puede truncar.

Algunas combinaciones que probé (rw, rw +, r + w, etc.) parece que no funcionan. ¿Es posible?

Algunos doc de Rubí (se aplica a pitón también):

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. 
+0

Así que básicamente quiere sobrescribir una archivo y asegúrese de que los contenidos anteriores no se mantengan por debajo del punto en que dejó de escribir? –

+0

Sí. 'file.truncate()' funciona para este propósito y funciona cuando el archivo es "r +", "w", "w +". Pero todos tienen los defectos que enumeré arriba. – ceztko

Respuesta

28

Según OpenGroup:

O_TRUNC

Si el archivo existe y es un archivo regular, y el archivo es éxito abrió O_RDWR o O_WRONLY, su longitud se trunca a 0 y el modo y el propietario no ha cambiado. No tendrá ningún efecto en los archivos especiales FIFO ni en los archivos del dispositivo terminal. Su efecto en otros tipos de archivos es según la implementación. El resultado de usar O_TRUNC con O_RDONLY es undefined.

Entonces, O_TRUNC probablemente se pasa al abrir un archivo con "w" o "w +". Esto le da a "truncamiento" un significado diferente, no lo que yo quiero.

Con python, la solución parece abrir el archivo en la E/S de bajo nivel con la función os.open().

La siguiente función de 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) 

tiene el comportamiento que quería. Se puede utilizar de esta manera (que es de hecho mi caso de uso):

# 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() 
+0

abriendo con os.O_RDWR lo abre en modo de solo lectura conmigo en python 2.6 en linux – rutherford

+1

Estoy realmente agitado por el uso de la palabra "truncar" como se aplica a los archivos. Truncar siempre ha significado (para mí) algo así como "clip corto". Es claramente diferente en significado (nuevamente, para mí) de "sobrescribir". –

+0

@StevenLu, estoy de acuerdo con usted. De todos modos, lo importante es que el comportamiento deseado se puede obtener sin demasiado esfuerzo. Este código era necesario, al igual que Ivo, para escribir un bloqueo de servidor con un contexto. No puedo recordar ahora, pero me pareció no deseable almacenar el contexto/información en un archivo diferente. Tal vez solo quería imitar el comportamiento de los archivos /var/run/foo.pid, que a menudo se usan como bloqueos y almacenar el pid del daemon actualmente en ejecución. – ceztko

0

no sé de ninguna manera elegante de hacer exactamente esto en Ruby. Mi solución probablemente sería crear un archivo temporal, escribir contenido en él y luego cambiarle el nombre al nombre que realmente quería. Esto sobrescribiría el archivo anterior si existe, o crearía el archivo si no existiera. Algo como esto:

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) 

El cambio de nombre elevará SystemCallError si falla por cualquier razón.

13

Bueno, solo existen estos modos, y todos ellos tienen los "defectos" que enumeró.

Su única opción es envolver open(). ¿Por qué no algo como esto? (Python)

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

se comporta como abierta, incluso se podría volver a enlazar a abrir() si realmente desea.

todas las características de abiertos se conservan, incluso se puede hacer:

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

Se podría, por supuesto, crear una ContextManager que se abre el archivo en un modo +, lo lee en la memoria, e intercepta escribe lo que maneja el truncamiento creando mágicamente un archivo temporal en el modo w, y cambia el nombre de ese archivo temporal a tu archivo original cuando lo cierras, pero supongo que eso supondría un exceso.

+1

+1 porque agregó algunas pistas importantes. Creo que encontré una mejor respuesta que (posiblemente) está sujeta a menos condiciones de carrera. Viene pronto. – ceztko

+0

Agregué mi respuesta. – ceztko

+0

ah, ya veo. Busqué algo así, pero no lo encontré. buen descubrimiento, si eso funciona, ¡genial! – ch3ka

2

se puede leer, escribir y truncar con "A +" (Rubí):

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 
+0

Estoy usando Python, pero el comportamiento parece idéntico en comparación con ruby. Si bien es cierto que 'truncado (tamaño)' funciona, 'truncado()' sin argumentos no funciona de la misma manera en el modo de adición (al menos en Linux). Mi solución alcanza el comportamiento exacto que quería. – ceztko

+0

En ruby ​​'truncado' debe tener un argumento. 'f.truncate (f.pos)' haría algo como "truncar aquí!". – steenslag

+0

Bueno, cierto :) Otro pequeño inconveniente es que en el modo de adición, la posición inicial es el final del archivo, no el comienzo. – ceztko

Cuestiones relacionadas