Cách nhanh nhất để kiểm tra xem một tệp có tồn tại bằng C ++ / C ++ 11 / C tiêu chuẩn không?


453

Tôi muốn tìm cách nhanh nhất để kiểm tra xem một tệp có tồn tại trong C ++ 11, C ++ hoặc C. chuẩn hay không. Tôi có hàng ngàn tệp và trước khi thực hiện chúng, tôi cần kiểm tra xem tất cả chúng có tồn tại không. Tôi có thể viết gì thay vì /* SOMETHING */trong chức năng sau đây?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}

2
boost::filesystemdường như sử dụng stat(). (Giả sử từ tài liệu.) Tôi không nghĩ bạn có thể thực hiện nhanh hơn nhiều cho các cuộc gọi FS. Cách để thực hiện những gì bạn đang làm nhanh là "tránh xem hàng ngàn tệp."
millimoose

16
Câu hỏi TOCTOU : làm thế nào để bạn biết tệp không được liên kết giữa kiểm tra tồn tại () của bạn và "làm gì đó trên đó" ?
hương

7
@pilcrow Điểm hay, nhưng có một phạm vi ứng dụng khá rộng mà không cần nhiều tính chính xác. Ví dụ, git pushcó lẽ không bận tâm để đảm bảo bạn không chạm vào cây làm việc sau lần kiểm tra bẩn ban đầu.
millimoose

9
'Tôi không thể nghĩ đến việc triển khai C / C ++ sẽ không có nó' - Windows không cung cấp môi trường POSIX.
Jim Balter

Câu trả lời:


778

Vâng, tôi đã cùng nhau tạo ra một chương trình thử nghiệm chạy từng phương thức này 100.000 lần, một nửa cho các tệp tồn tại và một nửa trên các tệp không.

#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); 
}

Kết quả cho tổng thời gian để chạy 100.000 cuộc gọi trung bình trên 5 lần chạy,

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**

Các stat()chức năng cung cấp hiệu suất tốt nhất trên hệ thống của tôi (Linux, biên soạn với g++), với một tiêu chuẩn fopengọi là đặt cược tốt nhất của bạn nếu bạn vì một lý do từ chối sử dụng các chức năng POSIX.


31
Không có phương pháp nào ở trên kiểm tra sự tồn tại, mà là khả năng tiếp cận. Tuy nhiên, tôi không biết một cách tiêu chuẩn C hoặc C ++ để kiểm tra sự tồn tại.
IInspectable

10
stat()dường như để kiểm tra sự tồn tại
el.pescado

105
Bất cứ ai sử dụng điều này cần phải nhớ #include <sys / stat.h> nếu không, nó sẽ cố gắng sử dụng sai stat.
Katianie

23
Tôi tưởng tượng cho phương thức ifstream, bạn không cần f.close()vì f đi ra khỏi phạm vi ở cuối hàm. Vậy return f.good()có thể thay thế ifkhối?
ilent2

11
Bạn cũng có thể sử dụng / test en.cppreference.com/w/cpp/experimental/fs/exists từ tiêu chuẩn sắp tới
zahir

153

Lưu ý: trong C ++ 14 và ngay sau khi hệ thống tập tin TS sẽ kết thúc và được thông qua, giải pháp sẽ là sử dụng:

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

và kể từ C ++ 17, chỉ:

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

5
đã có sẵn trong Boost.Filesystem
TemplateRex

1
Trong MS Visual Studio 2013, chức năng này khả dụng trongstd::tr2::sys::exists("helloworld.txt");
Constantin

3
Tôi thực sự hy vọng nó sẽ không như vậy std::exists, điều đó sẽ khá khó hiểu (nghĩ: tồn tại trong một container STL giống như một bộ).
einpoklum 17/2/2016

3
Cũng trong Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile 16/2/2017

1
Đừng quên#include <experimental/filesystem>
Mohammed Noureldin

112

Tôi sử dụng đoạn mã này, nó hoạt động tốt với tôi cho đến nay. Điều này không sử dụng nhiều tính năng ưa thích của C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}

8
Tuy nhiên, nó có thể thất bại nếu tập tin bị khóa bởi một chương trình khác hoặc nếu không có quyền truy cập vào tập tin.
Máy bay phản lực

2
bạn có cần đóng luồng không?
Mo0gles

29
@ Mo0gles: Trình ifstreamhủy sẽ được gọi khi thoát is_file_existvà nó sẽ đóng luồng.
Isaac

2
Kể từ C ++ 11, bạn có thể thực hiện điều đó trong một dòng bằng toán tử bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen

6
@Orwellophilereturn std::ifstream(fileName);
emlai

27

Nó phụ thuộc vào nơi tập tin cư trú. Chẳng hạn, nếu tất cả chúng đều nằm trong cùng một thư mục, bạn có thể đọc tất cả các mục nhập thư mục vào bảng băm và sau đó kiểm tra tất cả các tên so với bảng băm. Điều này thể nhanh hơn trên một số hệ thống hơn là kiểm tra từng tệp riêng lẻ. Cách nhanh nhất để kiểm tra từng tệp riêng lẻ tùy thuộc vào hệ thống của bạn ... nếu bạn đang viết ANSI C, cách nhanh nhất là fopenvì đó là cách duy nhất (một tệp có thể tồn tại nhưng không thể mở được, nhưng bạn có thể thực sự muốn mở nếu bạn cần phải "làm một cái gì đó trên nó"). C ++, POSIX, Windows đều cung cấp các tùy chọn bổ sung.

Trong khi tôi đang ở đó, hãy để tôi chỉ ra một số vấn đề với câu hỏi của bạn. Bạn nói rằng bạn muốn cách nhanh nhất và bạn có hàng ngàn tệp, nhưng sau đó bạn yêu cầu mã cho một hàm để kiểm tra một tệp duy nhất (và hàm đó chỉ hợp lệ trong C ++, không phải C). Điều này mâu thuẫn với các yêu cầu của bạn bằng cách đưa ra một giả định về giải pháp ... một trường hợp của vấn đề XY . Bạn cũng nói "trong tiêu chuẩn c ++ 11 (hoặc) c ++ (hoặc) c" ... hoàn toàn khác nhau và điều này cũng không phù hợp với yêu cầu của bạn về tốc độ ... giải pháp nhanh nhất sẽ liên quan đến việc điều chỉnh mã theo hệ thống mục tiêu. Sự không nhất quán trong câu hỏi được nhấn mạnh bởi thực tế là bạn đã chấp nhận một câu trả lời đưa ra các giải pháp phụ thuộc vào hệ thống và không phải là tiêu chuẩn C hoặc C ++.


25

Đối với những người thích boost:

 boost::filesystem::exists(fileName)

5
Boost thường cực kỳ chậm.
Serge Rogatch

4
Đối với hầu hết các ứng dụng, tệp tồn tại kiểm tra không phải là hiệu năng quan trọng
anhoppe

29
Không phải tất cả các khía cạnh của một ứng dụng hiệu suất cao đều yêu cầu tối ưu hóa. Ví dụ, đọc dòng lệnh hoặc tệp cấu hình có thể phức tạp và có thể không yêu cầu tốc độ, mặc dù bản thân ứng dụng có thể yêu cầu các lợi thế về hiệu suất của C ++. Tránh Boost trong những trường hợp như vậy cấu thành sự tái tạo bánh xe, nằm trong danh sách chống mẫu.
evoskuil

5
@SergeRogatch boost :: filesystem :: tồn tại không quá chậm. Xem kết quả điểm chuẩn của tôi để biết thông tin chi tiết.
hungptit

3
"Boost thường cực kỳ chậm" - điều này là sai và thậm chí còn không rõ phạm vi của khiếu nại là gì ... Boost chứa nhiều gói của các tác giả khác nhau nhưng được đánh giá cao về chất lượng. "Đối với hầu hết các ứng dụng, kiểm tra tồn tại tệp không quan trọng về hiệu năng" - OP đặc biệt yêu cầu tốc độ do kiểm tra số lượng tệp rất lớn. "Nếu hiệu suất không quan trọng, thì cũng không có điểm nào trong việc sử dụng C ++" - một nhận xét sai lầm khác (và lạc đề). Hầu hết các phần mềm được viết trong các cửa hàng và là một phần của hệ thống bắt buộc phải lựa chọn ngôn ngữ.
Jim Balter

23

Không sử dụng các thư viện khác, tôi muốn sử dụng đoạn mã sau:

#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;
}

Điều này hoạt động đa nền tảng cho các hệ thống tuân thủ Windows và POSIX.


Cái này có hoạt động trên Mac không? Tôi không có máy mac, nhưng tôi cũng mong máy mac có thể bao gồm unistd.hcả. Có lẽ đầu tiên #ifdefnên là cửa sổ cụ thể?
matth

5
Mac OSX tương thích POSIX.
schaiba

20

Tương tự như đề xuất của PherricOxide nhưng trong C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}

1
.c_str () là một hàm C ++. Tôi không biết C ++ vì vậy tôi đã đăng một C tương đương.
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.
}

19
Nếu bạn thực sự sẽ làm điều đó, chỉ cần "return (bool) file" thay vì sử dụng nhánh if / other.
Nik Haldimann

Đừng quên đóng tệp trong trường hợp đúng. Đó là một loại rò rỉ bộ nhớ nếu bạn để tệp mở trong toàn bộ thời gian chạy của chương trình, chưa kể nó có thể khóa tệp của bạn để bạn không thể đọc nó sau khi biết rằng nó tồn tại .. add: file.close () để thứ hai khác.
Bill Moore

2
trong suy nghĩ thứ hai, có lẽ bạn không cần phải đóng nó một cách rõ ràng ... Tôi quên rằng ifux là một RAII (Khởi tạo tài nguyên là khởi tạo) ... và sẽ tự dọn sạch khi nó vượt ra khỏi phạm vi của ... Tôi có thể nói ... Tôi bị tẩy não bởi ngôn ngữ của người thu gom rác những ngày này ...
Bill Moore

@BillMoore Nhận xét thứ hai của bạn là chính xác; nhiều ý kiến ​​khác trên trang này đã lưu ý close()là không cần thiết.
Keith M

Điều này kiểm tra khả năng tiếp cận, không tồn tại. Ví dụ: nếu tệp tồn tại, nhưng không thể truy cập do quyền truy cập, nó sẽ trả về sai, tuyên bố nhầm rằng tệp không tồn tại.
SasQ

7

3 tùy chọn khác dưới cửa sổ:

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;
}

OpenFile chỉ có ANSI và giới hạn ở 128 ký tự .
David Bremner

5
Các GetFileAttributesphiên bản về cơ bản là một cách kinh điển để làm điều đó trong Windows.
Felix Dombek

Tôi biết điều này đã cũ nhưng điều gì sẽ xảy ra trong trường hợp thứ 3 khi người dùng có khả năng đọc tệp nhưng không được phép đọc các thuộc tính tệp?
Nhiệm vụ

6

Bạn cũng có thể làm bool b = std::ifstream('filename').good();. Không có các hướng dẫn chi nhánh (như nếu), nó phải thực hiện nhanh hơn vì nó cần được gọi hàng ngàn lần.


Như câu trả lời được chấp nhận cho thấy, điều này là không đúng sự thật. Bất kỳ trình biên dịch nghiêm trọng có thể sẽ phát ra các mã như nhau cho dù bạn đặt trong nếu hay không. So với các biến thể plain-C, việc xây dựng đối tượng ifstream (ngay cả khi trên stack) phát sinh thêm chi phí.
minexew 7/2/2016

5

Nếu bạn cần phân biệt giữa một tệp và một thư mục, hãy xem xét những điều sau đây cả hai đều sử dụng công cụ tiêu chuẩn nhanh nhất như được trình bày bởi 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;
}

4

Tôi cần một chức năng nhanh có thể kiểm tra xem một tệp có tồn tại hay không và câu trả lời của PherricOxide gần như là những gì tôi cần ngoại trừ nó không so sánh hiệu năng của boost :: filesystem :: tồn tại và các hàm mở. Từ kết quả điểm chuẩn, chúng ta có thể dễ dàng thấy rằng:

  • Sử dụng chức năng stat là cách nhanh nhất để kiểm tra nếu một tập tin tồn tại. Lưu ý rằng kết quả của tôi phù hợp với câu trả lời của PherricOxide.

  • Hiệu năng của hàm boost :: filesystem :: rất gần với chức năng của stat và nó cũng có thể mang theo được. Tôi muốn giới thiệu giải pháp này nếu các thư viện boost có thể truy cập được từ mã của bạn.

Kết quả điểm chuẩn thu được với nhân Linux 4.17.0 và 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

Dưới đây là mã điểm chuẩn của tôi:

#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();   

4

Bạn có thể sử dụng std::ifstream, funcion như is_open, failví dụ như sau mã (các cout có nghĩa là "mở" tập tin tồn tại hay không):

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

trích dẫn từ câu trả lời này


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

đâu Rlà chuỗi các thứ giống như đường dẫn của bạn, và exists()là từ tiêu chuẩn tương lai hoặc tăng hiện tại. Nếu bạn tự cuộn, hãy giữ nó đơn giản,

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

Giải pháp phân nhánh không hoàn toàn khủng khiếp và nó sẽ không ngấu nghiến mô tả tập tin,

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
}

PathFileExistsđược giới hạn ở MAX_PATH(260) ký tự; GetFileAttributeskhông có giới hạn này.
Felix Dombek

GetFileAttributescũng bị giới hạn ở MAX_PATH. Các tài liệu mô tả một cách giải quyết nếu bạn sử dụng các đường dẫn tuyệt đối, unicode và thêm một chuỗi tiền tố đặc biệt vào tên đường dẫn. Tôi nghĩ rằng dù sao chúng ta cũng đang tiếp tục với các phản hồi dành riêng cho Windows.
John

1
GetFileAttributesWkhông có giới hạn.
Laurie Stearn

1

Trong 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);
}

5
Điều này ít thông tin hơn câu trả lời của Vincent 4 năm trước.
Jim Balter

2
Trong hệ thống tập tin C ++ 17 không còn thử nghiệm
Quest

0

Có thể sử dụng MFC với những điều sau đây

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

Đâu FileNamelà một chuỗi đại diện cho tệp bạn đang kiểm tra sự tồn tại


0

Chỉ có một cách nhanh hơn để kiểm tra xem tệp có tồn tại hay không và nếu bạn có quyền đọc nó thì cách sử dụng ngôn ngữ C mong muốn nhanh hơn và cũng có thể được sử dụng trong bất kỳ phiên bản nào trong C ++

Giải pháp : trong C có một thư viện errno.h có một biến số nguyên (toàn cầu) bên ngoài được gọi là errno chứa một số có thể được sử dụng để nhận ra loại lỗi

    #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;
    }

-4

Mặc dù có một số cách để làm điều này, giải pháp hiệu quả nhất cho vấn đề của bạn có lẽ là sử dụng một trong những phương pháp được xác định trước của fux, chẳng hạn như good () . Với phương pháp này, bạn có thể kiểm tra xem tệp bạn đã chỉ định có tồn tại hay không.

fstream file("file_name.txt");

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

Tôi hy vọng bạn thấy nó hữu dụng.


4
Mã này sẽ tạo tệp nếu nó không tồn tại, vì vậy kết quả sẽ luôn đúng. Bạn cần sử dụng ifstream hoặc đặt tham số openmode chính xác.
Lubo Antonov
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.