2012-04-27 16 views
31

chcę:Jak otworzyć (odczytać-zapisać) lub utworzyć plik z dozwolonym obcięciem?

  • otworzyć plik w trybie read-write, jeśli istnieje;
  • utworzyć, jeśli nie istnieje;
  • być w stanie skrócić go w dowolnym miejscu i czasie.

EDIT: ze skróconej rozumiem napisać aż pozycji i usunąć pozostałą część pliku, jeśli jest obecny

Wszystko to atomowo (z jednym open() rozmowy lub symulowanie jeden open() połączenia)

Żaden pojedynczy otwarty modalność wydaje się zastosowanie:

  • r: oczywiście nie działa;
  • r +: zawiedzie, jeśli plik nie istnieje;
  • w: odtworzyć plik, jeśli istnieje;
  • w +: odtworzyć plik, jeśli istnieje;
  • a: nie można odczytać;
  • a +: nie można obciąć.

Niektóre kombinacje, które wypróbowałem (rw, rw +, r + w, itp.) Również nie działają. Czy to możliwe?

Niektóre doc z Ruby (dotyczy pytona zbyt):

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

Więc zasadniczo chcesz zastąpić plik i upewnij się, że poprzednia zawartość nie pozostaje poniżej punktu, w którym przestałeś pisać? –

+0

Tak. 'file.truncate()' działa w tym celu i działa, gdy plik jest "r +", "w", "w +". Ale wszystkie mają wady wymienione powyżej. – ceztko

Odpowiedz

28

Według OpenGroup:

O_TRUNC

Jeśli plik istnieje i jest zwykłym plikiem, a plik jest skutecznie otworzył O_RDWR lub O_WRONLY, jego długość jest obcinana do 0 i tryb a właściciel pozostaje niezmieniony. Nie będzie to miało wpływu na pliki specjalne FIFO lub pliki urządzeń końcowych. Jego wpływ na inne typy plików jest zależny od implementacji od . Wynikiem użycia O_TRUNC z O_RDONLY jest niezdefiniowana .

Tak więc, O_TRUNC prawdopodobnie przeszedł podczas otwierania pliku z "w" lub "w +". Daje to "obcięcie" innego znaczenia, a nie tego, czego chcę.

W przypadku Pythona rozwiązanie wydaje się otwierać plik na niskim poziomie we/wy z funkcją os.open().

Poniższa funkcja 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) 

ma zachowanie chciałem. Można go używać tak (to w rzeczywistości mój przypadek użycia):

# 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

Otwieranie za pomocą os.O_RDWR otwiera go w trybie tylko do odczytu ze mną na pythonie 2.6 w Linuksie – rutherford

+1

Jestem naprawdę poruszony przez użycie słowa "przyciąć", jak stosuje się do plików. Truncate zawsze oznaczało (dla mnie) coś w stylu "short clip". Jest wyraźnie różny w znaczeniu (znowu dla mnie) od "nadpisania". –

+0

@StevenLu, całkowicie się z tobą zgadzam. W każdym razie ważne jest to, że pożądane zachowanie można uzyskać bez większego wysiłku. Ten kod był potrzebny, podobnie jak Ivo, do napisania blokady serwera z kontekstem. Nie pamiętam teraz, ale nie było mi potrzebne przechowywanie kontekstu/informacji w innym pliku. Może po prostu chciałem naśladować zachowanie plików /var/run/foo.pid, które są często używane jako blokady i przechowują pid aktualnie uruchomionego demona. – ceztko

0

ja nie znam żadnego elegancki sposób, aby dokładnie to zrobić w Ruby. Moim rozwiązaniem byłoby prawdopodobnie utworzenie pliku tymczasowego, napisanie do niego treści, a następnie zmiana nazwy na nazwę pliku, której naprawdę chciałem. Spowoduje to nadpisanie poprzedniego pliku, jeśli istnieje, lub utworzenie pliku, jeśli nie istnieje. Coś takiego:

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) 

Zmień nazwę podniesie SystemCallError jeśli nie z jakiegokolwiek powodu.

13

Cóż, istnieją tylko te tryby i wszystkie z nich mają wymienione "usterki".

Twoja jedyna opcja to opakowanie open(). Dlaczego nie coś takiego? (Python)

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

zachowuje się podobnie jak otwarte, można nawet ponownie powiązać to open(), jeśli naprawdę chcesz.

wszystkich funkcji otwarte są zachowane, można nawet zrobić:

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

Można oczywiście stworzyć contextmanager który otwiera plik w trybie +, odczytuje go do pamięci i przechwytuje pisze więc obsługiwać obcinanie magicznie tworząc tymczasowy plik w trybie w, i zmienia nazwę pliku tymczasowego na oryginalny plik po jego zamknięciu, ale to chyba przesada.

+1

+1, ponieważ dodano kilka ważnych wskazówek. Myślę, że znalazłem lepszą odpowiedź, która jest (być może) poddana mniejszym warunkom wyścigowym. Nadchodzi wkrótce. – ceztko

+0

Dodano moją odpowiedź. – ceztko

+0

ah, rozumiem. Szukałem czegoś takiego, ale go nie znalazłem. miłe znalezisko, jeśli to faktycznie działa, całkiem fajnie! – ch3ka

2

można czytać, pisać i obciąć „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 
+0

Używam Pythona, ale zachowanie wydaje się identyczne w porównaniu do ruby. Chociaż prawdą jest, że 'truncate (size)' działa, 'truncate()' bez argumentów nie działa w taki sam sposób w trybie append (przynajmniej w systemie Linux). Moje rozwiązanie pozwala osiągnąć dokładnie to, czego chciałem. – ceztko

+0

W ruby ​​'truncate' musi mieć jeden argument. 'f.truncate (f.pos)' zrobi coś, co "skróci się tutaj!". – steenslag

+0

Cóż, prawda :) Inną małą pułapką jest to, że w trybie dopisywania pozycja początkowa jest końcem pliku, a nie początkiem. – ceztko