A maneira mais rápida de verificar se existe um arquivo usando C ++ / C ++ 11 / C padrão?

453

Gostaria de encontrar a maneira mais rápida de verificar se existe um arquivo no padrão C ++ 11, C ++ ou C. Tenho milhares de arquivos e antes de fazer algo neles preciso verificar se todos eles existem. O que posso escrever em vez de /* SOMETHING */na seguinte função?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Vincent
fonte
2
boost::filesystemparece usar stat(). (Assumindo a partir da documentação.) Não acho que você possa fazer muito mais rápido para chamadas de FS. A maneira de acelerar o que você está fazendo é "evitar a visualização de milhares de arquivos".
Millimoose
16
TOCTOU pergunta: como você sabe que o arquivo não está desvinculado entre a verificação exist () e o "fazendo algo nele" ?
Pilcrow
7
@pilcrow Bom argumento, mas há uma gama bastante ampla de aplicativos que não precisam de tanta correção. Por exemplo, git pushprovavelmente não se preocupa em garantir que você não esteja tocando na árvore de trabalho após a verificação inicial suja.
Millimoose
9
'Não consigo pensar em uma implementação C / C ++ que não a possua' - o Windows não fornece um ambiente POSIX.
Jim Balter

Respostas:

778

Bem, juntei um programa de teste que executou cada um desses métodos 100.000 vezes, metade em arquivos que existiam e metade em arquivos que não existiam.

#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <fstream>

inline bool exists_test0 (const std::string& name) {
    ifstream f(name.c_str());
    return f.good();
}

inline bool exists_test1 (const std::string& name) {
    if (FILE *file = fopen(name.c_str(), "r")) {
        fclose(file);
        return true;
    } else {
        return false;
    }   
}

inline bool exists_test2 (const std::string& name) {
    return ( access( name.c_str(), F_OK ) != -1 );
}

inline bool exists_test3 (const std::string& name) {
  struct stat buffer;   
  return (stat (name.c_str(), &buffer) == 0); 
}

Resultados do tempo total para executar as 100.000 chamadas em média em 5 execuções,

Method exists_test0 (ifstream): **0.485s**
Method exists_test1 (FILE fopen): **0.302s**
Method exists_test2 (posix access()): **0.202s**
Method exists_test3 (posix stat()): **0.134s**

A stat()função forneceu o melhor desempenho no meu sistema (Linux, compilado com g++), com uma fopenchamada padrão sendo sua melhor aposta se, por algum motivo, se recusar a usar as funções POSIX.

PherricOxide
fonte
31
Nenhum dos métodos acima verifica a existência, mas a acessibilidade. Porém, não conheço uma única maneira C ou C ++ padrão de verificar a existência.
IInspectable
10
stat()parece verificar a existência.
el.pescado
105
Qualquer pessoa que use isso precisa se lembrar de incluir # sys / stat.h>, caso contrário, tenta usar a estatística errada.
Katianie
23
Eu imagino que, para o método ifstream, você não precise, f.close()pois f sai do escopo no final da função. Então, return f.good()poderia substituir o ifbloco?
Ilent2
11
Você também pode usar / teste en.cppreference.com/w/cpp/experimental/fs/exists do próximo padrão
zahir
153

Observação: no C ++ 14 e assim que o TS do sistema de arquivos for concluído e adotado, a solução será usar:

std::experimental::filesystem::exists("helloworld.txt");

e desde C ++ 17, apenas:

std::filesystem::exists("helloworld.txt");
Vincent
fonte
5
já está disponível no Boost.Filesystem
TemplateRex
1
No MS Visual Studio 2013, esta função está disponível emstd::tr2::sys::exists("helloworld.txt");
Constantin
3
Na verdade, espero que não seja std::exists, isso seria bastante confuso (pense: existe em um contêiner STL como um conjunto).
einpoklum
3
Também no Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile 16/02
1
Não se esqueça de#include <experimental/filesystem>
Mohammed Noureldin
112

Eu uso esse pedaço de código, ele funciona bem comigo até agora. Isso não usa muitos recursos sofisticados do C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
harryngh
fonte
8
No entanto, pode falhar se o arquivo foi bloqueado por outro programa ou se não houver acesso ao arquivo.
Jet
2
você precisa fechar o fluxo?
Mo0gles
29
@ Mo0gles: O ifstreamdestruidor será chamado ao sair is_file_existe fechará o fluxo.
Isaac
2
A partir de C ++ 11 você poderia fazê-lo em uma linha usando o operador booleano: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen
6
@Orwellophilereturn std::ifstream(fileName);
emlai
27

Depende de onde os arquivos residem. Por exemplo, se todos eles deveriam estar no mesmo diretório, você pode ler todas as entradas do diretório em uma tabela de hash e depois verificar todos os nomes na tabela de hash. Isso pode ser mais rápido em alguns sistemas do que verificar cada arquivo individualmente. A maneira mais rápida de verificar cada arquivo individualmente depende do seu sistema ... se você estiver escrevendo ANSI C, a maneira mais rápida éfopen porque é a única maneira (um arquivo pode existir, mas não pode ser aberto, mas você provavelmente realmente deseja abrir se você precisa "fazer algo nele"). C ++, POSIX, Windows oferecem opções adicionais.

Enquanto estou nisso, deixe-me apontar alguns problemas com sua pergunta. Você diz que deseja o caminho mais rápido e possui milhares de arquivos, mas solicita o código de uma função para testar um único arquivo (e essa função é válida apenas em C ++, não em C). Isso contradiz seus requisitos, assumindo a solução ... um caso do problema XY . Você também diz "no padrão c ++ 11 (ou) c ++ (ou) c" ... que são todos diferentes, e isso também é inconsistente com o seu requisito de velocidade ... a solução mais rápida envolveria a adaptação do código ao sistema de destino. A inconsistência na pergunta é destacada pelo fato de você ter aceitado uma resposta que fornece soluções que dependem do sistema e não são C ou C ++ padrão.

Jim Balter
fonte
25

Para quem gosta de impulso:

 boost::filesystem::exists(fileName)
anhoppe
fonte
5
O aumento é geralmente extremamente lento.
Serge Rogatch 27/08/16
4
Para a maioria das aplicações existe um arquivo de verificação não é desempenho crítico
anhoppe
29
Nem todos os aspectos de um aplicativo de alto desempenho exigem otimização. Por exemplo, a leitura da linha de comando ou de um arquivo de configuração pode ser complexa e não exigir velocidade, embora o próprio aplicativo possa exigir as vantagens de desempenho do C ++. Evitar o Boost nesses casos constitui reinvenção da roda, no topo da lista antipadrão.
evoskuil
5
O @SergeRogatch boost :: filesystem :: existe não é extremamente lento. Veja meus resultados de benchmark para informações detalhadas.
Hungptit 5/0518
3
"O Boost geralmente é extremamente lento" - isso é falso e nem está claro qual é o escopo da reivindicação ... O Boost contém muitos pacotes de autores diferentes, mas é avaliado por alta qualidade. "Para a maioria dos aplicativos, existe uma verificação de arquivo que não é essencial para o desempenho" - o OP pediu especificamente velocidade devido à verificação de um número muito grande de arquivos. "Se o desempenho não for crítico, também não há sentido em usar C ++" - outro comentário incorreto (e fora de tópico). A maioria dos softwares é escrita em lojas e faz parte de um sistema que exige a escolha do idioma.
21819 Jim Balter
23

Sem usar outras bibliotecas, eu gosto de usar o seguinte trecho de código:

#ifdef _WIN32
   #include <io.h> 
   #define access    _access_s
#else
   #include <unistd.h>
#endif

bool FileExists( const std::string &Filename )
{
    return access( Filename.c_str(), 0 ) == 0;
}

Isso funciona em várias plataformas para sistemas compatíveis com Windows e POSIX.

Viktor Liehr
fonte
Isso funciona no Mac? Eu não tenho um Mac, mas eu esperaria que um Mac pudesse incluir unistd.htambém. Talvez o primeiro #ifdefdeva ser específico do Windows?
Matth
5
O Mac OSX é compatível com POSIX.
schaiba
20

O mesmo que sugerido por PherricOxide, mas em C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}
Ramon La Pietra
fonte
1
.c_str () é uma função C ++. Eu não sei C ++, então eu postei um equivalente em C.
Ramon La Pietra
10
inline bool exist(const std::string& name)
{
    ifstream file(name);
    if(!file)            // If the file was not found, then file is 0, i.e. !file=1 or true.
        return false;    // The file was not found.
    else                 // If the file was found, then file is non-0.
        return true;     // The file was found.
}
LOLOLOL
fonte
19
Se você realmente quiser fazer isso, basta "retornar (bool) arquivo" em vez de usar um ramo if / else.
precisa saber é o seguinte
Não se esqueça de fechar o arquivo no caso de um caso real. Esse é um tipo de vazamento de memória se você deixar o arquivo aberto durante todo o tempo de execução do programa, sem mencionar que ele pode bloquear o arquivo, para que você não possa lê-lo depois de saber que ele existe .. add: file.close () para segundo mais.
Bill Moore
2
pensando bem, talvez você não precise fechá-lo explicitamente ... Eu esqueci que o ifstream é um RAII (Aquisição de recursos é inicialização) ... e se limpará à medida que sai do escopo do destruidor ... o que posso dizer ... Eu me lavagem cerebral por línguas coletor de lixo nos dias de hoje ...
Bill Moore
@BillMoore Seu segundo comentário está correto; muitos outros comentários nesta página notaram que close()não é necessário.
Keith M
Isso verifica acessibilidade, não existência. Por exemplo, se o arquivo existir, mas não puder ser acessado devido a direitos de acesso, ele retornará falso, alegando erroneamente que o arquivo não existe.
SasQ 16/02/19
7

Outras 3 opções no Windows:

1

inline bool exist(const std::string& name)
{
    OFSTRUCT of_struct;
    return OpenFile(name.c_str(), &of_struct, OF_EXIST) != INVALID_HANDLE_VALUE && of_struct.nErrCode == 0;
}

2

inline bool exist(const std::string& name)
{
    HANDLE hFile = CreateFile(name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != NULL && hFile != INVALID_HANDLE)
    {
         CloseFile(hFile);
         return true;
    }
    return false;
}

3

inline bool exist(const std::string& name)
{
    return GetFileAttributes(name.c_str()) != INVALID_FILE_ATTRIBUTES;
}
ravin.wang
fonte
O OpenFile é apenas ANSI e limitado a 128 caracteres .
David Bremner
5
A GetFileAttributesversão é basicamente a maneira canônica de fazer isso no Windows.
Felix Dombek
Eu sei que isso é antigo, mas o que acontecerá no terceiro caso quando o usuário puder ler o arquivo, mas não puder ler os atributos do arquivo?
Quest
6

Você também pode fazer bool b = std::ifstream('filename').good();. Sem as instruções de ramificação (como se), ele deve ter um desempenho mais rápido, pois precisa ser chamado milhares de vezes.

parv
fonte
Como mostra a resposta aceita, isso é falso. Qualquer compilador sério provavelmente emitirá o mesmo código, independentemente de você colocar o if ou não. Comparado às variantes C simples, a construção do objeto ifstream (mesmo que na pilha) incorre em sobrecarga adicional.
minexew
5

Se você precisar distinguir entre um arquivo e um diretório, considere o seguinte: ambos usam stat, que é a ferramenta padrão mais rápida, conforme demonstrado pelo PherricOxide:

#include <sys/stat.h>
int FileExists(char *path)
{
    struct stat fileStat; 
    if ( stat(path, &fileStat) )
    {
        return 0;
    }
    if ( !S_ISREG(fileStat.st_mode) )
    {
        return 0;
    }
    return 1;
}

int DirExists(char *path)
{
    struct stat fileStat;
    if ( stat(path, &fileStat) )
    {
        return 0;
    }
    if ( !S_ISDIR(fileStat.st_mode) )
    {
        return 0;
    }
    return 1;
}
user3902302
fonte
4

Eu preciso de uma função rápida que possa verificar se um arquivo existe ou não e a resposta do PherricOxide é quase o que eu preciso, exceto que não compara o desempenho das funções boost :: filesystem :: existentes e abertas. A partir dos resultados do benchmark, podemos ver facilmente que:

  • Usar a função stat é a maneira mais rápida de verificar se existe um arquivo. Observe que meus resultados são consistentes com os da resposta do PherricOxide.

  • O desempenho da função boost :: filesystem :: existe é muito próximo ao da função stat e também é portátil. Eu recomendaria esta solução se as bibliotecas de impulso estiverem acessíveis no seu código.

Resultados de benchmark obtidos com o kernel Linux 4.17.0 e gcc-7.3:

2018-05-05 00:35:35
Running ./filesystem
Run on (8 X 2661 MHz CPU s)
CPU Caches:
  L1 Data 32K (x4)
  L1 Instruction 32K (x4)
  L2 Unified 256K (x4)
  L3 Unified 8192K (x1)
--------------------------------------------------
Benchmark           Time           CPU Iterations
--------------------------------------------------
use_stat          815 ns        813 ns     861291
use_open         2007 ns       1919 ns     346273
use_access       1186 ns       1006 ns     683024
use_boost         831 ns        830 ns     831233

Abaixo está o meu código de referência:

#include <string.h>                                                                                                                                                                                                                                           
#include <stdlib.h>                                                                                                                                                                                                                                           
#include <sys/types.h>                                                                                                                                                                                                                                        
#include <sys/stat.h>                                                                                                                                                                                                                                         
#include <unistd.h>                                                                                                                                                                                                                                           
#include <dirent.h>                                                                                                                                                                                                                                           
#include <fcntl.h>                                                                                                                                                                                                                                            
#include <unistd.h>                                                                                                                                                                                                                                           

#include "boost/filesystem.hpp"                                                                                                                                                                                                                               

#include <benchmark/benchmark.h>                                                                                                                                                                                                                              

const std::string fname("filesystem.cpp");                                                                                                                                                                                                                    
struct stat buf;                                                                                                                                                                                                                                              

// Use stat function                                                                                                                                                                                                                                          
void use_stat(benchmark::State &state) {                                                                                                                                                                                                                      
    for (auto _ : state) {                                                                                                                                                                                                                                    
        benchmark::DoNotOptimize(stat(fname.data(), &buf));                                                                                                                                                                                                   
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_stat);                                                                                                                                                                                                                                          

// Use open function                                                                                                                                                                                                                                          
void use_open(benchmark::State &state) {                                                                                                                                                                                                                      
    for (auto _ : state) {                                                                                                                                                                                                                                    
        int fd = open(fname.data(), O_RDONLY);                                                                                                                                                                                                                
        if (fd > -1) close(fd);                                                                                                                                                                                                                               
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_open);                                  
// Use access function                                                                                                                                                                                                                                        
void use_access(benchmark::State &state) {                                                                                                                                                                                                                    
    for (auto _ : state) {                                                                                                                                                                                                                                    
        benchmark::DoNotOptimize(access(fname.data(), R_OK));                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_access);                                                                                                                                                                                                                                        

// Use boost                                                                                                                                                                                                                                                  
void use_boost(benchmark::State &state) {                                                                                                                                                                                                                     
    for (auto _ : state) {                                                                                                                                                                                                                                    
        boost::filesystem::path p(fname);                                                                                                                                                                                                                     
        benchmark::DoNotOptimize(boost::filesystem::exists(p));                                                                                                                                                                                               
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_boost);                                                                                                                                                                                                                                         

BENCHMARK_MAIN();   
hungptit
fonte
4

Você pode usar std::ifstream, como is_open, failpor exemplo, o código abaixo (o cout "aberto" significa que o arquivo existe ou não):

insira a descrição da imagem aqui

insira a descrição da imagem aqui

citado nesta resposta

Jayhello
fonte
3
all_of (begin(R), end(R), [](auto&p){ exists(p); })

onde Restá a sua sequência de coisas semelhantes a caminhos e exists()é a partir do futuro std ou atual impulso. Se você criar o seu próprio, seja simples,

bool exists (string const& p) { return ifstream{p}; }

A solução ramificada não é absolutamente terrível e não devora os descritores de arquivos,

bool exists (const char* p) {
    #if defined(_WIN32) || defined(_WIN64)
    return p && 0 != PathFileExists (p);
    #else
    struct stat sb;
    return p && 0 == stat (p, &sb);
    #endif
}
John
fonte
PathFileExistsestá limitado a MAX_PATH(260) caracteres; GetFileAttributesnão tem essa limitação.
Felix Dombek
GetFileAttributestambém está limitado a MAX_PATH. Os documentos descrevem uma solução alternativa se você usar caminhos absolutos, unicode e acrescentar uma sequência de prefixos especial ao nome do caminho. Acho que estamos de acordo com as respostas específicas do Windows.
John John
1
GetFileAttributesWnão tem a limitação.
22718 Laurie Stearn
1

No C ++ 17:

#include <experimental/filesystem>

bool is_file_exist(std::string& str) {   
    namespace fs = std::experimental::filesystem;
    fs::path p(str);
    return fs::exists(p);
}
Abhijeet Kandalkar
fonte
5
Isso é menos informativo do que a resposta dada por Vincent 4 anos antes.
Jim Balter
2
Em C ++ 17, o sistema de arquivos não é mais experimental
Quest
0

Usando o MFC, é possível com os seguintes

CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);

Onde FileNameestá uma string representando o arquivo que você está verificando quanto à existência

Andy Bantly
fonte
0

existe apenas uma maneira mais rápida de verificar se o arquivo existe e se você tem permissão para lê-lo da maneira que está usando a linguagem C, o desejo é mais rápido e pode ser usado também em qualquer versão do C ++

solução : em C, há uma biblioteca errno.h que possui uma variável inteira (global) externa chamada errno, que contém um número que pode ser usado para reconhecer o tipo de erro

    #include <stdio.h>
    #include <stdbool.h>
    #include <errno.h>

    bool isFileExist(char fileName[]) {
        FILE *fp = fopen(fileName, "r");
        if (fp) {
            fclose(fp);
            return true;
        }
        return errno != ENOENT;
    }

    bool isFileCanBeRead(char fileName[]) {
        FILE *fp = fopen(fileName, "r");
        if (fp) {
            fclose(fp);
            return true;
        }
        return errno != ENOENT && errno != EPERM;
    }
Imad Diraa
fonte
-4

Embora existam várias maneiras de fazer isso, a solução mais eficiente para o seu problema provavelmente seria usar um dos métodos predefinidos do fstream, como good () . Com esse método, você pode verificar se o arquivo especificado existe ou não.

fstream file("file_name.txt");

if (file.good()) 
{
    std::cout << "file is good." << endl;
}
else 
{
    std::cout << "file isnt good" << endl;
}

Espero que você ache isso útil.

miksiii
fonte
4
Esse código criará o arquivo se ele não existir, portanto o resultado será sempre verdadeiro. Você precisa usar ifstream ou definir o parâmetro openmode corretamente.
Lubo Antonov