2012-01-24 20 views
23

Sto cercando di ottenere il file di un file di grandi dimensioni (12 gb +) e non voglio aprire il file per farlo, dato che presumo questo mangerebbe un sacco di risorse. C'è qualche buona API con cui farlo? Sono in un ambiente Windows.Controllare i file senza aprire il file in C++?

risposta

1

Che dire della funzione GetFileSize?

+2

Ciò richiede l'apertura del file, che l'OP non è auspicabile. –

+0

@remy ma il file è dove la dimensione è memorizzata in modo che le due richieste nella domanda siano contraddittorie –

+0

In realtà no, il file stesso non memorizza la dimensione. Il filesystem lo memorizza. 'GetFileSize()' richiede che il file sia aperto per primo, quindi usa quell'impugnatura per determinare dove si trova il file nel filesystem in modo che possa prendere la dimensione. Se si usa 'FindFirstFile()', interroga il filesystem senza bisogno di aprire il file. –

41

È necessario chiamare GetFileSizeEx che è più facile da usare rispetto al precedente GetFileSize. Avrai bisogno di aprire il file chiamando lo CreateFile ma è un'operazione a basso costo. La tua ipotesi che l'apertura di un file sia costosa, anche un file da 12 GB, è falsa.

è possibile utilizzare la seguente funzione per ottenere il lavoro fatto:

__int64 FileSize(const wchar_t* name) 
{ 
    HANDLE hFile = CreateFile(name, GENERIC_READ, 
     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 
     FILE_ATTRIBUTE_NORMAL, NULL); 
    if (hFile==INVALID_HANDLE_VALUE) 
     return -1; // error condition, could call GetLastError to find out more 

    LARGE_INTEGER size; 
    if (!GetFileSizeEx(hFile, &size)) 
    { 
     CloseHandle(hFile); 
     return -1; // error condition, could call GetLastError to find out more 
    } 

    CloseHandle(hFile); 
    return size.QuadPart; 
} 

Non ci sono altre chiamate API che restituirà la dimensione del file, senza costringere a creare un handle file, in particolare GetFileAttributesEx. Tuttavia, è perfettamente plausibile che questa funzione aprirà il file dietro le quinte.

__int64 FileSize(const wchar_t* name) 
{ 
    WIN32_FILE_ATTRIBUTE_DATA fad; 
    if (!GetFileAttributesEx(name, GetFileExInfoStandard, &fad)) 
     return -1; // error condition, could call GetLastError to find out more 
    LARGE_INTEGER size; 
    size.HighPart = fad.nFileSizeHigh; 
    size.LowPart = fad.nFileSizeLow; 
    return size.QuadPart; 
} 

Se si compila con Visual Studio e si vuole evitare di chiamare API Win32 è possibile utilizzare _wstat64.

Ecco una versione _wstat64 base della funzione:

__int64 FileSize(const wchar_t* name) 
{ 
    __stat64 buf; 
    if (_wstat64(name, &buf) != 0) 
     return -1; // error, could use errno to find out more 

    return buf.st_size; 
} 

Se le prestazioni mai diventato un problema per voi, allora si dovrebbe cronometrare le varie opzioni su tutte le piattaforme che come bersaglio al fine di raggiungere una decisione. Non dare per scontato che le API che non richiedono di chiamare CreateFile saranno più veloci. Potrebbero esserlo ma non lo saprai finché non lo avrai cronometrato.

+0

Naturalmente, '' CreateFile() 'può essere piuttosto lento se si sta aprendo il file su supporti lenti] (http://blogs.msdn.com/b/larryosterman/archive/2004/05/24/140396. aspx) come unità di rete, ma la lentezza sarebbe dovuta alle latenze di accesso allo storage e non a causa del fatto che il file è enorme. –

+0

@Insilico O unità nastro! Ma credo che l'apertura del file sia l'unico modo per trovare la dimensione del file, almeno su Windows. –

+0

@DavidHeffernan: No! La dimensione del file è nell'intestazione e quindi nella directory. FindFirstFile() come mostrato di seguito leggerà queste informazioni senza dover aprire il file. –

9

Un'altra opzione utilizzando la funzione di FindFirstFile

#include "stdafx.h" 
#include <windows.h> 
#include <tchar.h> 
#include <stdio.h> 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    WIN32_FIND_DATA FindFileData; 
    HANDLE hFind; 
    LPCTSTR lpFileName = L"C:\\Foo\\Bar.ext"; 

    hFind = FindFirstFile(lpFileName , &FindFileData); 
    if (hFind == INVALID_HANDLE_VALUE) 
    { 
     printf ("File not found (%d)\n", GetLastError()); 
     return -1; 
    } 
    else 
    { 
     ULONGLONG FileSize = FindFileData.nFileSizeHigh; 
     FileSize <<= sizeof(FindFileData.nFileSizeHigh) * 8; 
     FileSize |= FindFileData.nFileSizeLow; 
     _tprintf (TEXT("file size is %u\n"), FileSize); 
     FindClose(hFind); 
    } 
    return 0; 

} 
+0

Utilizzare 'ULARGE_INTEGER' invece di girare manualmente i bit' ULONGLONG', ad es .: 'ULARGE_INTEGER ul; ul.LowPart = FindFileData.nFileSizeLow; ul.HighPart = FindFileData.nFileSizeHigh; ULONGLONG FileSize = ul.QuadPart; '. Inoltre, '% u' si aspetta un' unsigned int' a 32 bit su Windows, è necessario usare '% Lu' invece per un numero intero a 64 bit. –

+2

Credo che FindFirstFile recuperi le dimensioni del file come registrato nella voce della directory. Si noti che in alcune circostanze ciò potrebbe non essere accurato, ad es. Se il file è hard link ed è stato modificato tramite un diverso hard link, o se un'altra applicazione ha il file aperto e lo ha modificato. Vedi http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx –

+1

Presumibilmente il problema a cui Harry fa riferimento è il motivo per cui la RTL Delphi ha smesso di utilizzare FindFirstFile nella sua funzione sys della dimensione del file. –

23

Ho anche vissuto con la paura del prezzo pagato per l'apertura di un file e la chiusura solo per ottenere la sua dimensione. E ha deciso di chiedere allo performance counter^ e vedere quanto costose siano davvero le operazioni.

Questo è il numero di cicli necessari per eseguire 1 query di dimensioni file sullo stesso file con i tre metodi. Testato su 2 file: 150 MB e 1,5 GB. Hai fluttuazioni del +/- 10% in modo che non sembrino influenzate dalle dimensioni effettive del file. (ovviamente questo dipende da CPU ma ti dà un buon punto di osservazione)

  • 190 cicli - CreateFile, GetFileSizeEx, CloseHandle
  • 40 cicli - GetFileAttributesEx
  • 150 cicli - FindFirstFile, FindClose

The GIST with the code used^è disponibile qui.

Come possiamo vedere da questo test altamente scientifico :), il più lento è in realtà l'apri file. Il secondo più lento è il file finder mentre il vincitore è il lettore degli attributi. Ora, in termini di affidabilità, l'CreateFile deve essere preferito rispetto all'altro 2. Ma non mi piace ancora il concetto di aprire un file solo per leggere le sue dimensioni ... A meno che non stia facendo cose di dimensioni critiche, io Andiamo per gli attributi .

PS: Quando avrò tempo cercherò di leggere le dimensioni dei file aperti e in cui sto scrivendo. Ma non adesso ...

0

A partire da C++ 17, c'è il numero file_size come parte della libreria standard. (Quindi l'implementatore deve decidere come farlo in modo efficiente!)