programing

표준 C++/C++11, 14, 17/C를 사용하여 파일이 존재하는지 확인하는 가장 빠른 방법?

prostudy 2022. 6. 26. 09:50
반응형

표준 C++/C++11, 14, 17/C를 사용하여 파일이 존재하는지 확인하는 가장 빠른 방법?

표준 C++11, 14, 17 또는 C에 파일이 있는지 가장 빨리 확인하고 싶습니다.수천 개의 파일이 있는데, 그 파일을 처리하기 전에 모든 파일이 존재하는지 확인해야 합니다. 제가 뭘 쓸 수 요?/* SOMETHING */음음음음음음?

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

저는 각각의 방법을 100,000번씩 실행하는 테스트 프로그램을 만들었습니다. 절반은 존재하는 파일이고 절반은 존재하지 않는 파일입니다.

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

5회 동안 평균 100,000개의 콜을 실행하기 위한 총 시간 결과

방법 시간을
exists_test0유효) 0.485초
exists_test1(파일 열기) 0.302초
exists_test2 access (posix access()) 0.140s
exists_test3 stat (posix stat()) 0.134초

stat()이 함수는 내 시스템에서 최고의 성능을 제공합니다(Linux, 컴파일,g++ (), (표준)fopen어떤 이유로 POSIX 기능 사용을 거부한 경우 전화하는 것이 최선의 선택입니다.

비고: C++14에서는 파일 시스템 TS가 완료되어 채택되는 즉시 다음과 같은 솔루션을 사용할 수 있습니다.

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

C++17 이후만:

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

나는 이 코드를 사용하지만, 지금까지는 잘 작동한다.이것은 C++의 화려한 기능을 많이 사용하지 않습니다.

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

Boost를 원하는 사용자:

 boost::filesystem::exists(fileName)

또는 ISO C++17 이후:

 std::filesystem::exists(fileName)

다른 라이브러리를 사용하지 않고 다음 코드 스니펫을 사용합니다.

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

이 기능은 Windows 및 POSIX 호환 시스템에서 크로스 플랫폼 방식으로 작동합니다.

파일의 존재 여부를 확인할 수 있는 빠른 기능이 필요합니다.PherricOxide의 답변은 boost:: filesystem의 퍼포먼스를 비교하지 않는 것을 제외하고 거의 필요한 것입니다.:기능을 선택하여 엽니다.벤치마크 결과를 보면, 다음과 같은 것을 쉽게 알 수 있습니다.

  • stat 함수를 사용하는 것이 파일이 존재하는지 확인하는 가장 빠른 방법입니다.제 결과는 페릭옥시드의 답변과 일치합니다.

  • 부스트 성능:: 파일 시스템::stat 함수는 stat 함수에 매우 가깝고 휴대성도 뛰어납니다.코드로부터 부스트 라이브러리에 액세스 할 수 있는 경우는, 이 솔루션을 추천합니다.

Linux 커널 4.17.0 및 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

벤치마크 코드는 다음과 같습니다.

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

PherricOxide에서 제안하는 것과 동일하지만 C에서 권장된다.

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

다른 모든 답변은 모든 파일을 개별적으로 확인하는 데 초점을 맞춥니다. 그러나 파일이 모두 하나의 디렉토리(폴더)에 있는 경우에는 디렉토리를 읽고 원하는 모든 파일 이름이 있는지 확인하는 이 훨씬 더 효율적일 수 있습니다.

이것은, 파일이 복수의 디렉토리에 분산되어 있는 경우에서도, 디렉토리와 파일의 비율에 따라서는 보다 효율적일 수 있습니다.각 타겟 파일이 독자적인 디렉토리에 있는 것, 또는 체크하고 싶지 않은 다른 파일이 같은 디렉토리에 많이 있는 것에 가까워지면, 최종적으로 각 파일을 개별적으로 체크하는 것보다 효율이 저하될 것으로 예상됩니다.

좋은 휴리스틱: 이미 보유하고 있는 많은 데이터를 처리하는 것이 운영체제에 데이터를 요구하는 것보다 훨씬 빠릅니다.시스템 호출 오버헤드는 개별 기계 명령에 비해 매우 큽니다.따라서 OS에 "이 디렉토리에 있는 모든 파일 목록을 달라"고 요청하고 목록을 검토하는 것이 거의 항상 빠릅니다. OS에 "이 파일에 대한 정보를 달라", "이 파일에 대한 정보를 달라", "이 파일에 대한 정보를 달라", "이 파일에 대한 정보를 달라"고 요청하는 것이 느립니다.

모든 양호한 C 라이브러리는 버퍼링된 I/O와 마찬가지로 효율적인 방법으로 "디렉토리 내의 모든 파일에 반복" API를 구현합니다.내부적으로는 각 엔트리에 대해 OS에 개별적으로 물어보는 것처럼 보이지만 OS에서 디렉토리 엔트리의 큰 목록을 한 번에 읽어냅니다.


그래서 만약 내가 이 조건을 가지고 있다면

  1. 모든 파일이 한 폴더에 있고 다른 파일은 해당 폴더에 없을 수 있도록 설계와 사용을 장려하기 위해 가능한 모든 것을 수행합니다.
  2. O(1) 또는 적어도 O(log(n) 룩업 및 삭제 시간이 있는 메모리 내의 데이터 구조(해시 맵이나 바이너리 트리 등)에 표시해야 할 파일 이름 목록을 넣습니다.
  3. 해당 디렉토리의 파일을 나열하고 메모리의 "리스트"(해시 맵 또는 바이너리 트리)에서 이동할 때 각 파일을 "체크오프"(삭제)합니다.

정확한 사용 사례에 따라서는 해시 맵이나 트리에서 엔트리를 삭제하는 대신 각 엔트리에 대해 "Do I have this file?" 모든 파일을 가지고 있습니까?이치노바이너리 트리가 있을 수 있지만 각 비리프 노드의 구조에는 리프 노드의 부울과 논리적인 부울도 있습니다.한 후 각 의 "을 "have this로 설정합니다.리프 노드에서 부울을 설정한 후 트리 위로 이동하여 각 노드의 "have this?" 부울을 설정합니다.&&하위 노드의 부울(다른 하위 노드를 반복할 필요가 없습니다. 리프 중 하나를 true로 설정하기 위해 매번 이 프로세스를 일관되게 수행할 경우 해당 하위 노드의 모든 하위 노드가 true로 설정될 경우에만 true로 설정됩니다.)


안타깝게도 C++17까지는 표준적인 방법이 없습니다.

C++17은 취득했습니다.

그에 하는 것이 .boost::filesystem::directory_iterator이전 버전의 C++에서는 동작할 것으로 생각됩니다.

은 'C'입니다.opendir그리고 그리고.readdir부터에서dirent.h이것은 표준 C 인터페이스입니다.C 표준 자체가 아니라 POSIX로 표준화되어 있을 뿐입니다.Mac OS, Linux, 모든 BSD, 기타 UNIX/UNIX 유사 시스템 및 기타 POSIX/SUS 시스템에서 즉시 사용할 수 있습니다.Windows 의 경우는, 인크루드 패스에 다운로드해 드롭 하면 되는 실장이 있습니다.

단, 가장 빠른 방법을 찾고 계시기 때문에 휴대용/표준 제품보다 더 나은 방법을 찾는 것이 좋을 수도 있습니다.

리눅스에서는, 당신은 수동으로 원시스템 리눅스 에서는 원시스템 호출을 사용하여 버퍼 크기를 수동으로 지정하여 성능을 최적화할 수 있습니다 콜과 버퍼 크기를 지정하여 당신의 성능을 최적화할 수 있을 것이다.getdents64.

Windows 에서는, 좀 더 자세히 조사해 보면, 사용하고 싶은 퍼포먼스를 최대한으로 끌어낼 수 있을 것 같습니다.FindFirstFileExA함께와FindExInfoBasic그리고 그리고.FIND_FIRST_EX_LARGE_FETCH언젤 수 있는 오픈 소스 도서관의 위처럼 많은 오픈 소스 라이브러리의 많은 부분이 위와 같은 경우.dirent.hWindows の windows windows windows windows windows windows windows windows windows windows windows windows windows windows windows windows windows 。, 의 버전보다 는, 인 「이다」, 「Windows 」를 이 좋습니다.FindFirstFile★★★★★★★★★★★★★★★★★★★★★★★★★★★

는 위의 않을 것이며,9번 플랜을 될 것입니다.또한 이 플랜에서는dirread ★★★★★★★★★★★★★★★★★」dirreadall(디렉토리 컨텐츠 전체에 충분한 메모리가 있는 것을 안전하게 가정할 수 있는 경우는, 후자).의 버퍼 를 보다 는, 플레인 「」을 사용해 주세요.read ★★★★★★★★★★★★★★★★★」read디렉토리 엔트리의 데이터를 디코딩합니다.이것은 문서화된 머신에 의존하지 않는 형식이며, 도우미 기능이 제공되고 있다고 생각합니다.

다른 운영체제는 모릅니다.


나중에 몇 가지 테스트를 통해 이 답을 편집할 수 있습니다.테스트 결과에서도 편집할 수 있는 것도 있습니다.

파일이 어디에 있느냐에 따라 다릅니다.예를 들어, 모든 엔트리가 같은 디렉토리에 있는 경우 모든 디렉토리 엔트리를 해시 테이블로 읽어낸 후 해시 테이블과 대조하여 모든 이름을 체크할 수 있습니다.일부 시스템에서는 각 파일을 개별적으로 확인하는 것보다 이 속도가 더 빠를 수 있습니다.각 파일을 개별적으로 확인하는 가장 빠른 방법은 시스템에 따라 다릅니다.ANSI C를 쓰는 경우 가장 빠른 방법은fopen유일한 방법이기 때문입니다(파일은 존재하지만 열 수는 없지만 "뭔가" 해야 할 경우에는 열 수 있는 파일이 필요할 수 있습니다).C++, POSIX, Windows 모 c c c 。

내친김에 당신의 질문에 몇 가지 문제점을 지적하겠습니다.가장 빠른 방법을 원하며 수천 개의 파일이 있다고 하지만 단일 파일을 테스트하기 위한 함수의 코드를 요구합니다(이 함수는 C가 아닌 C++에서만 유효합니다).이는 XY 문제의 경우 솔루션에 대해 가정함으로써 귀사의 요구 사항에 부합하지 않습니다.「in standard c++11(또는) c++(또는) c」라고 하는 경우도 있습니다.모두 다르고, 이 또한 속도에 대한 요구 사항과 일치하지 않습니다.가장 빠른 해결책은 대상 시스템에 맞춰 코드를 조정하는 것입니다.질문의 불일치는 시스템에 의존하며 표준 C 또는 C++가 아닌 솔루션을 제공하는 답변을 수락했다는 사실에서 두드러집니다.

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

여기 간단한 예가 있습니다!

#include <iostream>
#include <fstream>
using namespace std;
    
void main(){
   SearchFile("test.txt");
}

bool SearchFile(const char *file)
{
   ifstream infile(file);
   if (!infile.good())
   {
    // If file is not there
    exit(1);
   }
}

하시면 됩니다.std::ifstream , , , 같은 is_open,fail예를 들어 다음과 같은 코드입니다(cout "open"은 파일의 존재 여부를 나타냅니다).

여기에 이미지 설명 입력

여기에 이미지 설명 입력

대답에서 인용한.

더 쉬운 방법이 있어

#include <fstream>
#include <iostream>

void FileExists(std::string myfile){
std::ifstream file(myfile.c_str());

if (file) {
    std::cout << "file exists" << std::endl;
}
else {
    std::cout << "file doesn't exist" << std::endl;
}
}

int main() {
FileExists("myfile.txt");

return 0;
}

파일과 디렉토리를 구별할 필요가 있는 경우는, PherricOxide 로 실증되고 있는 가장 빠른 표준 툴의 stat 를 사용하는 것을 고려해 주세요.

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

도 이렇게 돼요.bool b = std::ifstream('filename').good();브런치 명령(예를 들어, 브런치 명령)이 없으면 수천 번 호출이 필요하기 때문에 더 빨리 동작해야 합니다.

파일 존재 여부를 테스트하는 가장 빠르고 안전한 방법은 따로 또는 명시적으로 테스트하지 않는 것입니다.즉, 일반적인 것을 대체할 수 있는 방법을 찾아보세요.

if(exists(file)) {                           /* point A */
    /* handle existence condition */
    return;
}

do_something_with(file);                     /* point B */

개선과 함께

r = do_something_with_unless_exists(file);

if(r == 0)
    success;
else if(errno == EEXIST)
    /* handle existence condition */
else
    /* handle other error */

이것에 의해, 고속이 되는 것 외에, 최초의 솔루션에 고유의 레이스 조건(특히 「TOC/TOU」), 즉 포인트A와 포인트B 사이에 파일이 존재할 가능성이 없어집니다.

두 으로 할 수 것을 로 하고 .do_something_with_unless_exists 때도 찾아 경우도 있습니다.종종 방법이 있지만, 가끔은 그걸 찾아 헤매야 할 때도 있어요.

  • 작성: call " " : callopen()O_CREAT ★★★★★★★★★★★★★★★★★」O_EXCL.

  • 순수 C로 작성: C11: Call C11: Callfopen()"wx"(「」의 「」의 「」입니다.

  • 작성: call 디렉 making : call만 making making making making makingmkdir()errno == EEXIST★★★★★★★★★★★★★★★★★★.

  • 자물쇠 획득: 소금 값어치의 자물쇠 시스템은 이미 원자적인 획득을 가지고 있습니다.

(다른 것도 있지만, 지금 생각나는 것은 그것뿐입니다.)

: : ]할 수 특정 경우 directory the 디렉토리를 되었습니다.따라서 뮤텍스를 셋업하려면 일반적으로 특정 빈 디렉토리를 작성해야 합니다.mkdir은 항상 또는 않는 것에 할 수 있는 있습니다.] syscall은 이전 존재 또는 존재하지 않는 것을 기반으로 합니다.

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

창 아래의 다른 3가지 옵션:

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;
}
all_of (begin(R), end(R), [](auto&p){ exists(p); })

서 ''는R 것의입니다.exists()미래 표준 또는 현재 부스트에서 온 것입니다.하게 해 심플하게 해 주세요, 심플하게, 심플하게 해 주세요.

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

분기 솔루션은 완전히 나쁜 것은 아니며 파일 기술자를 게걸스럽게 먹지 않습니다.

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
}

윈도에 파일이 있는지 감지하는 중입니다.

bool DoesExistFile(std::wstring filePath)
{
    bool result = true;

    HANDLE fileHandle = CreateFile(
        filePath.c_str(),
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if ((fileHandle != NULL) && (fileHandle != INVALID_HANDLE_VALUE))
        CloseHandle(fileHandle);
    else
    {
        DWORD error = GetLastError();

        if ((error == ERROR_FILE_NOT_FOUND) || (error == ERROR_PATH_NOT_FOUND))
            result = false;
    }

    return result;
}

이를 위한 몇 가지 방법이 있지만 가장 효율적인 문제 해결 방법은 good()와 같은 fstream의 사전 정의된 방법 중 하나를 사용하는 것입니다.이 방법을 사용하면 지정한 파일이 존재하는지 여부를 확인할 수 있습니다.

fstream file("file_name.txt");

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

이게 도움이 됐으면 좋겠어요.

MFC를 사용하면 다음과 같이 할 수 있습니다.

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

서 ★★★★★FileName는 존재 를 확인하는 입니다.

파일이 존재하는지, 읽을 수 있는 권한이 있는지 확인할 수 있는 가장 빠른 방법은 하나뿐입니다. C 언어를 사용하는 방법이 더 빠르고 C++의 어떤 버전에서도 사용할 수 있습니다.

해결책: C에는 라이브러리 errno.h가 있습니다. 라이브러리 errno는 errno라는 이름의 외부(글로벌) 정수 변수를 가지고 있습니다.이 변수에는 오류 유형을 인식하기 위해 사용할 수 있는 번호가 포함됩니다.

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

언급URL : https://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exists-using-standard-c-c11-14-17-c

반응형