2013-06-13 7 views
5

Uruchom następujący kod w Pythonie 2.7.5. pod Windows:Czas modyfikacji pliku nie jest równy po wywołaniu shutil.copystat (file1, file2) pod Windows

import os, shutil, stat, time 

with open('test.txt', 'w') as f: pass # create an arbitrary file 
shutil.copy('test.txt', 'test2.txt') # copy it 
shutil.copystat('test.txt', 'test2.txt') # copy its stats, too 

t1 = os.lstat('test.txt').st_mtime # get the time of last modification for both files 
t2 = os.lstat('test2.txt').st_mtime 

print t1 # prints something like: 1371123658.54 
print t2 # prints the same string, as expected: 1371123658.54 
print t1 == t2 # prints False! Why?! 

Spodziewam oba znaczniki czasu (= pływaków) do równe (jak sugerują ich reprezentacje smyczkowych), więc dlaczego t1 == t2 ocenić na False?

Ponadto nie udało mi się odtworzyć tego zachowania przy użyciu mniejszego kodu, tj. Bez porównywania znaczników czasu pobranych za pomocą os.lstat z dwóch różnych różnych plików. Mam wrażenie, jestem brakuje czegoś trywialnego tutaj ...


Edit: Po dalszych testów zauważyłem, że robi odcisk True raz na jakiś czas, ale nie częściej niż raz na 10 działa.


Edit 2: Jak sugeruje larsmans:

print ("%.7f" % t1) # prints e.g. 1371126279.1365688 
print ("%.7f" % t2) # prints e.g. 1371126279.1365681 

Rodzi to dwa nowe pytania:

  1. Dlaczego nie równa znaczniki czasu po wywołaniu shutil.copystat?
  2. print rundy są domyślnie pływające ?!
+0

Nie można odtworzyć w systemie Debian Linux. Spróbuj 'print ("% .7f "% t1)' i '(t1 - t2) <1e-4', aby sprawdzić, czy" mały wydruk "jest inny. –

+0

Aby zobaczyć, dlaczego rundy drukowania są domyślnie wyświetlane, spróbuj tego: 'x = 10.1 (nowa linia) print ("% .20f "% x)'. To wydrukuje coś innego niż myślisz. Jest to normalne, ponieważ wartości zmiennoprzecinkowe nie mogą dokładnie reprezentować wszystkich wartości ułamkowych. Nie mam pojęcia o problemie z zamkniętymi drzwiami. –

+0

Wiem o reprezentacji binarnej, ale tutaj przypadek jest inny: Mamy float o wartości '10.099999' i wypisuje' 10.1' chociaż nie określiliśmy formatu takiego jak '% .2f' - więc wydaje się, że niejawne zaokrąglenia w efekcie, o których nie wiedziałem i których nigdy wcześniej nie zauważyłem ... –

Odpowiedz

5

Problem jest z konwersji pomiędzy różnymi formatami podczas rozmowy copystat. Dzieje się tak, ponieważ system Windows przechowuje czasy plików w formacie dziesiętnym o stałym punkcie, a Python przechowuje je w formacie binarnym zmiennoprzecinkowym. Tak więc za każdym razem, gdy następuje konwersja między tymi dwoma formatami, tracona jest pewna dokładność.Podczas copystat rozmowy:

  1. Wywołanie os.stat zamienia formacie Windows do formatu zmiennoprzecinkowych Pythona. Pewna dokładność jest tracona.
  2. os.utime jest wywoływana, aby zaktualizować czas pliku. to zamienia go z powrotem na format Windows. Pewna dokładność jest tracona ponownie, a czas pliku niekoniecznie jest taki sam jak pierwszego pliku.

Po wywołaniu os.lstat użytkownik wykonuje trzecią niedokładną konwersję. Z powodu tych konwersji czasy plików nie są dokładnie takie same.

documentation for os.utime wspomina o tym:

Należy pamiętać, że dokładne razy tutaj zawarte nie mogą być zwrócone przez kolejny stat() połączenia, w zależności od rozdzielczości, dzięki którym Twój system operacyjny dostępu rekordy i modyfikacja razy


Odnośnie drugiego pytania (dlaczego print wydaje się pokazać te same wartości dla obu): Konwersja wartości zmiennoprzecinkowej na łańcuch z str(f) lub print f zaokrągla wartość. Aby uzyskać gwarantowaną wartość unikalną dla różnych wartości zmiennoprzecinkowych, należy zamiast tego użyć print repr(f).

-1

Spróbuj wydrukować różnicę czasową w zmiennoprzecinkowych: print float.hex(t1 - t2)

wyniki:

0x1.0000000000000p-22 
0x1.8000000000000p-21 
0x0.0p+0 
0x1.0000000000000p-20 

Moje 2cents przypuszczenie: zmienność wyjścia pochodzi z reprezentacji zmiennoprzecinkowej nastawienie i zaokrąglania błąd. W każdym razie, powinieneś zawsze używać wartości epsilon podczas porównywania dwóch zmiennych.

EDYCJA: sprawdź to Python bug.

To jest ograniczenie systemu. Podstawowy system plików obsługuje nanosekundową rozdzielczość dla znaczników plików, a stat (2) obsługuje je także . Jednak utime (2) obsługuje tylko mikrosekundę o rozdzielczości podczas ich ustawiania.

Musisz użyć rozdzielczości mikrosekund w najlepszym przypadku, porównując dwa pływaki.

+1

Tak, tym bardziej niepokojące jest to, że t1 jest większe niż t2, podczas gdy powinno być odwrotnie (tekst2 jest nowszy niż tekst1) – lucasg

+0

Ten błąd raport dotyczy systemu Linux. Implementacja Pythona dla Windows nie używa 'utimes', więc nie ma takiego mikrosekundowego ograniczenia. – interjay

+0

GetFileTime (http://msdn.microsoft.com/en-us/library/windows/desktop/ms724320 (v = vs.85) .aspx) ma rozdzielczość 10 milisekund, więc zakładam, że shutil w systemie Windows nie może być lepszy. (Sprawdziłem w kodzie Pythona i lstat zadzwonię do winapi GetFileAttributesExW) – lucasg

0
from decimal import * 
print Decimal(t1) 
print Decimal(t2) 

Zastosowanie round() dla t1 i t2