I'm trying to figure out how to work this thing out .. For some reason, it ends at a certain point.. I'm not very good at recursion and I'm sure the problem lies somewhere there..
Also, even if I checked for cFileName != "..", it still shows up at the end, not sure why but the "." doesn't show up anymore..
void find_files( wstring wrkdir )
{
wstring temp;
temp = wrkdir + L"\\" + L"*";
fHandle = FindFirstFile( temp.c_str(), &file_data );
if( fHandle == INVALID_HANDLE_VALUE )
{
return;
}
else
{
while( FindNextFile( fHandle, &file_data ) )
{
if( file_data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY &&
wcscmp(file_data.cFileName, L".") != 0 &&
wcscmp(file_data.cFileName, L"..") != 0 )
{
find_files( wrkdir + L"\\" + file_data.cFileName );
}
else if( file_data.dwFileAttributes != FILE_ATTRIBUTE_HIDDEN &&
file_data.dwFileAttributes != FILE_ATTRIBUTE_SYSTEM )
{
results << wrkdir << "\\" << file_data.cFileName << endl;
}
}
}
}
After changing those, the program doesn't enumerate the remaining files left..
For example, if there is a sub folder named test, it enumerates everything inside test but doesn't finish enumerating the files inside the original directory specified.
From the FindFirstFile documentation:
If the function fails or fails to
locate files from the search string in
the lpFileName parameter, the return
value is INVALID_HANDLE_VALUE and the
contents of lpFindFileData are
indeterminate.
You should only exit from the one iteration not the whole program:
if( fHandle == INVALID_HANDLE_VALUE )
{
return;
}
And this may solve your other problem:
else if( file_data.dwFileAttributes != FILE_ATTRIBUTE_HIDDEN &&
file_data.dwFileAttributes != FILE_ATTRIBUTE_SYSTEM &&
wcscmp(file_data.cFileName, L".") != 0 &&
wcscmp(file_data.cFileName, L"..") != 0
)
{
results << wrkdir << "\\" << file_data.cFileName << endl;
}
Also see #fretje's answer as well. It gives another problem that your code has.
Updated new: You need to use fHandle as a local variable as well, not global variable.
Change to:
HANDLE fHandle = FindFirstFile( temp.c_str(), &file_data );
You are changing the value of your local wrkdir variable:
wrkdir = wrkdir + L"\\" + file_data.cFileName;
find_files( wrkdir );
I think you have to call find_files there like this:
find_files( wrkdir + L"\\" + file_data.cFileName );
and not change the value of wrkdir.
There are still several bugs in your code. Try this instead:
void find_files( wstring wrkdir )
{
wstring wrkdirtemp = wrkdir;
if( !wrkdirtemp.empty() && (wrkdirtemp[wrkdirtemp.length()-1] != L'\\') )
{
wrkdirtemp += L"\\";
}
WIN32_FIND_DATA file_data = {0};
HANDLE hFile = FindFirstFile( (wrkdirtemp + L"*").c_str(), &file_data );
if( hFile == INVALID_HANDLE_VALUE )
{
return;
}
do
{
if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if( (wcscmp(file_data.cFileName, L".") != 0) &&
(wcscmp(file_data.cFileName, L"..") != 0) )
{
find_files( wrkdirtemp + file_data.cFileName );
}
}
else
{
if( (file_data.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0 )
{
results << wrkdirtemp << file_data.cFileName << endl;
}
}
}
while( FindNextFile( hFile, &file_data );
FindClose( hFile );
}
Recursive file search with dirent.h
#include <iostream>
#include <dirent.h>
#include <string.h>
bool isUpDirecory(const char* directory) {
if (strcmp(directory, "..") == 0 || strcmp(directory, ".") == 0)
return true;
else
return false;
}
bool findFile(const std::string& fileName, const std::string& path,
std::string& resultPath) {
dirent* entry;
DIR* dir = opendir(path.c_str());
if (dir == NULL)
return false;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_REG) {
if (fileName.compare(entry->d_name) == 0) {
resultPath = path + "/" + entry->d_name;
closedir(dir);
return true;
}
}
}
rewinddir(dir);
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
if (!isUpDirecory(entry->d_name)) {
std::string nextDirectoryPath = path + "/" + entry->d_name;
bool result = findFile(fileName, nextDirectoryPath, resultPath);
if (result == true) {
closedir(dir);
return true;
}
}
}
}
closedir(dir);
return false;
}
int main() {
std::string path;
bool result = findFile("text.txt", "/home/lamerman/", path);
std::cout << path << std::endl;
return 0;
}
Also, check out the implementation of the CFileFind MFC class.
You still have errors in your code:
you ignore the results of the first search. you call FindFirstFile and handle if it fails. But if it succeeds you do not process already fetched file_data and overwrite it with FindNextFile.
You don't close the search handle. Use FindClose for that.
From your existing code it seems that fHandle is global - it shouldn't. It would break your recursion.
Also I think that you can resolve all the issues in your code by paying more attention to MSDN sample provided in FindFirstFile documentation.
Related
My code:
QString clsDebugService::strGetUserFolder(QString strFolder) {
QString strCopy(strFolder);
if ( strCopy.isEmpty() ) {
strCopy = "~";
}
const QChar cqcSeperator(QDir::separator());
QString strFullPath;
//Ensure path is correct before use, this will handle translation of ~ if used!
const char* cpszFolder = strCopy.toLatin1().data();
wordexp_t exp_result;
if ( wordexp(cpszFolder, &exp_result, 0) == 0 ) {
char** ppszFullPath = exp_result.we_wordv;
if ( ppszFullPath != nullptr ) {
strFullPath = *ppszFullPath;
QFileInfo info(strFullPath);
if ( info.isDir() == true && strFullPath.endsWith(cqcSeperator) != true ) {
strFullPath += cqcSeperator;
}
}
}
return strFullPath;
}
If strFolder contains:
~/XMLMPAM/config/form.xml
Then the function works and ~ is replaced with the actual user folder location, however is strFolder contains:
~/XMLMPAM 2/config/form.xml
Which also exists, the function fails and I can see when debugging that:
char** ppszFullPath = exp_result.we_wordv;
Points to:
/Users/sy/XMLMPAM
Which is my user profile folder but with the wrong folder name. Why?
The actually fix for this:
QString clsDebugService::strGetUserFolder(QString strFolder) {
QString strCopy(strFolder);
if ( strCopy.indexOf(" ") != -1 ) {
//Ensure any spaces in the path are escaped
strCopy.replace(" ", "\\ ");
} else if ( strCopy.isEmpty() ) {
strCopy = "~";
}
const QChar cqcSeperator(QDir::separator());
QString strFullPath;
//Ensure path is correct before use, this will handle translation of ~ if used!
const char* cpszFolder = strCopy.toLatin1().data();
wordexp_t exp_result;
if ( wordexp(cpszFolder, &exp_result, 0) == 0 ) {
char** ppszFullPath = exp_result.we_wordv;
if ( ppszFullPath != nullptr ) {
strFullPath = *ppszFullPath;
QFileInfo info(strFullPath);
if ( info.isDir() == true && strFullPath.endsWith(cqcSeperator) != true ) {
strFullPath += cqcSeperator;
}
}
}
return strFullPath;
}
New to C++. I have a code that spins thru directories to look for a specific file (123.txt) and lists the result in a textbox. What i need to do is store these result in memory so i can access it later. An array maybe? I'm not exactly sure how that's done.
Here's the code to execute it:
outfile1.open("pxutil1.log");
DWORD dwSize = MAX_PATH;
char szLogicalDrives[MAX_PATH];
DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);
if (dwResult == 0)
{
// error handling...
}
else if (dwResult > MAX_PATH)
{
// not enough buffer space...
}
else
{
for (char* szSingleDrive = szLogicalDrives; *szSingleDrive != 0; szSingleDrive += (lstrlenA(szSingleDrive) + 1))
{
if (GetDriveTypeA(szSingleDrive) == DRIVE_FIXED)
FindFile(szSingleDrive);
}
}
Here's the code to search for 123.txt and list the restult in a logfile.
std::string dir = directory;
if ((!dir.empty()) && (dir.back() != '\\') && (dir.back() != '/'))
dir += '\\';
WIN32_FIND_DATAA file;
HANDLE search_handle = FindFirstFileA((dir + "*").c_str(), &file);
if (search_handle == INVALID_HANDLE_VALUE)
{
if (GetLastError() != ERROR_FILE_NOT_FOUND)
{
// error handling...
//::MessageBox(NULL, "File not found", "", MB_OK);
}
}
else
{
do
{
if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((lstrcmpA(file.cFileName, ".") != 0) && (lstrcmpA(file.cFileName, "..") != 0))
{
//FindFile(dir + file.cFileName);
if (!ExcludeDir(dir + file.cFileName))
{
FindFile(dir + file.cFileName);
}
}
}
else
{
if (lstrcmpA(file.cFileName, "123.txt") == 0)
{
outfile1 << dir.c_str() << endl; //write to log file
}
}
} while (FindNextFileA(search_handle, &file));
if (GetLastError() != ERROR_NO_MORE_FILES)
{
::MessageBox(NULL, "No more files", "", MB_OK);
}
FindClose(search_handle);
}
I thought maybe i could add this to the code, but it doesn't work
string listOfDir[20]
std::string(dir) >> listOfDir;
The standard C++ language does not have overloads of operator>> to input arrays.
The following does not work:
std::cin >> listOfDir;
The C++ language does not have any functions to split a string.
The following does not work:
std::string(dir) >> listOfDir;
The compiler can't find any overload of operator>> that takes a std::string parameter and an array parameter. The statement would be equivalent of:
operator>>(std::string, std::string[]);
In summary, you'll need to write the code yourself to parse or split a string; or you can search the internet for a string library.
If you use std::vector, this code may be more useful:
std::vector<std::string> listOfDir;
//...
listOfDir.push_back(dir);
The above code fragment appends a copy of dir to the vector listOfDir.
I'm trying to use miniunzip to extract some files. It works on Linux. On Windows, it throws no errors, but if the file is executable, the resulting binary doesn't work. I get a message window with a message about incompatibility with 64-bit Windows. If I use another utility, such as 7-zip, to unpack it, everything works fine, so the problem is here in my code. Here is the class method that does all the work.
bool FileHandler::unzip( string inputFile, string outputDirectory )
{
if (!fileExists(inputFile)) {
this->errorMessage = "Can't find file at " + inputFile;
return false;
}
unzFile zipFile = unzOpen(inputFile.c_str());
if( zipFile == nullptr ){
this->errorMessage = "FileHandler::unzip failed to open input file";
return false;
}
vector<string> files;
vector<string> folders;
unz_global_info globalInfo;
int err = unzGetGlobalInfo( zipFile, &globalInfo );
if (unzGoToFirstFile(zipFile) != UNZ_OK) {
this->errorMessage = "FileHandler::unzip failed calling unzGoToFirstFile";
return false;
}
for ( unsigned long i=0; i < globalInfo.number_entry && err == UNZ_OK; i++ ){
char filename[FILENAME_MAX];
unz_file_info subFileInfo;
err = unzGetCurrentFileInfo( zipFile, &subFileInfo, filename,
sizeof(filename), NULL, 0, NULL, 0);
if ( err == UNZ_OK )
{
char nLast = filename[subFileInfo.size_filename-1];
if ( nLast =='/' || nLast == '\\' )
{
folders.push_back(filename);
}
else
{
files.push_back(filename);
}
err = unzGoToNextFile(zipFile);
}
}
for ( string & folder : folders ){
string strippedFolder = folder.substr(0, folder.length()-1);
string dirPath = normalizePath(outputDirectory+"/"+strippedFolder);
if( ! makeDirectory( dirPath ) ){
this->errorMessage = "FileHandler::unzip Failed to create directory "+dirPath;
return false;
}
}
for ( auto it = files.begin(); it != files.end(); it++ ){
if( zipFile == 0 ){
this->errorMessage = "FileHandler::unzip invalid unzFile object at position 1";
return false;
}
string filename = *it;
//string filepath = outputDirectory + "/" + *it;
string filepath = normalizePath( outputDirectory + "/" + *it );
const char * cFile = filename.c_str();
const char * cPath = filepath.c_str();
int err = unzLocateFile( zipFile, cFile, 0 );
if ( err != UNZ_OK ){
this->errorMessage = "FileHandler::unzip error locating sub-file.";
return false;
}
err = unzOpenCurrentFile( zipFile );
if( err != UNZ_OK ){
this->errorMessage = "FileHandler::unzip error opening current file";
return false;
}
ofstream fileStream{ cPath };
// Need an ostream object here.
if( fileStream.fail() ){
this->errorMessage = "FileHandler::unzip error opening file stream at "+string(cPath);
return false;
}
unz_file_info curFileInfo;
err = unzGetCurrentFileInfo( zipFile, &curFileInfo, 0, 0, 0, 0, 0, 0);
if ( err != UNZ_OK )
{
this->errorMessage = "FileHandler::unzip failed to read size of file";
return false;
}
unsigned int size = (unsigned int)curFileInfo.uncompressed_size;
char * buf = new char[size];
size = unzReadCurrentFile( zipFile, buf, size );
if ( size < 0 ){
this->errorMessage = "FileHandler::unzip unzReadCurrentFile returned an error. ";
return false;
}
fileStream.write( buf, size );
fileStream.flush();
delete [] buf;
fileStream.close();
#ifndef _WIN32
vector<string> parts = splitString(filename, ".");
if( parts.size() == 1 ){ // In linux, assume file without extension is executable
mode_t old_mask = umask( 000 );
chmod( cPath, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH );
umask( old_mask );
}
#endif
unzCloseCurrentFile( zipFile );
}
unzClose(zipFile);
return true;
}
std::ostream opens files in text mode by default, you need to make it use binary mode instead.
On Linux there doesn't seem to be any difference between text and binary modes. But on Windows, attempting to write \n into a text file produces \r\n, currupting your data.
You need to change this line
ofstream fileStream{ cPath };
to
ofstream fileStream{ cPath, ostream::out | ostream::binary };
I have a project which need to read path of SysData file.I want to move SysData file which contains "ç","ş","ğ" path way but cannot read this char.I have to read with UNICODE(like that utf-8).
There is code;
bool TSimTextFileStream::ReadLine ( mstring * str )
{
*str = "";
char c = ' ';
bool first = true;
// while ( read ( hFile, &c, 1 ) )
while ( fread ( &c, 1, 1, hFile ) )
{
if (first) first = false;
#ifdef __linux__
if ( c == 13 )
continue;
else
if ( c == 10 )
break;
else
*str += c;
#else
if( c == 13 || c == 10)
break;
else
*str += c;
#endif
}
return !first;
}
And there is code, calling this method;
mstring GetSysDataDirectory ( )
{
static mstring sysDataDir = "";
if ( sysDataDir == "" )
{
if (mIsEnvironmentVarExist("SYSDATAPATH"))
{
mstring folder = mGetEnvVar("SYSDATAPATH");
if (folder.size() == 0)
{
folder = mGetCurrentDir ( ) + "/SysData";
}
sysDataDir = folder;
}
else if ( mIsFileExist ( "SysDataPath.dat" ) )
{
TSimTextFileStream txtfile;
txtfile.OpenFileForRead( "SysDataPath.dat" );
mstring folder;
if ( txtfile.ReadLine( &folder ) )
{
sysDataDir = folder;
}
else
{
sysDataDir = mGetCurrentDir ( ) + "/SysData";
}
}
else
{
sysDataDir = mGetCurrentDir ( ) + "/SysData";
}
}
return sysDataDir;
}
I search and find some solution but not work, like that;
bool TSimTextFileStream::OpenFileForRead(mstring fname)
{
if (hFile != NULL) CloseFile();
hFile = fopen(fname.c_str(), "r,ccs=UNICODE");
if (hFile == NULL) return false; else return true;
}
and tried this;
hFile = fopen(fname.c_str(), "r,ccs=UTF-8");
But not work again. Can you help me please?
enter image description here
This situation is my problem :((
Windows does not support UTF-8 encoded path names for fopen:
The fopen function opens the file that is specified by filename. By
default, a narrow filename string is interpreted using the ANSI
codepage (CP_ACP).
Source.
Instead, a second function, called _wfopen is provided, which accepts a wide-character string as path argument.
Similar restrictions apply when using the C++ fstreams for File I/O.
So the only way for you to solve this is by converting your UTF-8 encoded pathname either to the system codepage or to a wide character string.
fopen usually reads unicode chars. try to change the files encoding
I am failing to see where i am going wrong. This current code skips straight to closefile. NOt processing any files, i may just be missing something obvious and it has been a long day.
My function is meant to search the hard disk (c:) for a given file. EG example.txt. &strFilePath here would be used in the FindFirstFile declaration.
Any help would be appeciated.
Thanks.
String Copy::SearchDrive( const String& strFile, const String& strFilePath, const bool& bRecursive, const bool& bStopWhenFound ) const
{
HANDLE hFile;
WIN32_FIND_DATA file;
hFile = FindFirstFile("C:\\", &file);
String strFoundFilePath = "";
if ( hFile )
{
while ( FindNextFile( hFile, &file))
{
String strTheNameOfTheFile = file.cFileName;
// It could be a directory we are looking at
// if so look into that dir
if ( file.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
&& bRecursive )
{
String strNewFilePath = strFilePath + "\\";
strNewFilePath += strTheNameOfTheFile;
SearchDrive( strFile, strNewFilePath, bRecursive, bStopWhenFound );
}
else
{
if ( strTheNameOfTheFile == strFile )
{
strFoundFilePath = strFilePath;
strFoundFilePath += "\\";
strFoundFilePath += strFile;
/// TODO
// ADD TO COLLECTION TYPE
if ( bStopWhenFound )
{
break;
}
}
}
}
CloseHandle( hFile );
}
return strFoundFilePath;
}
You have quite a few logic bugs in your code. Try this instead (you did not indicate which compiler you are using, so I am assuming C++Builder, which has an uppercase-S String class. Adjust the code as needed if you are using a different compiler):
String Copy::SearchDrive(const String& strFile, const String& strFilePath, const bool& bRecursive, const bool& bStopWhenFound) const
{
String strFoundFilePath;
WIN32_FIND_DATA file;
String strPathToSearch = strFilePath;
if (!strPathToSearch.IsEmpty())
strPathToSearch = IncludeTrailingPathDelimiter(strPathToSearch);
HANDLE hFile = FindFirstFile((strPathToSearch + "*").c_str(), &file);
if (hFile != INVALID_HANDLE_VALUE)
{
do
{
String strTheNameOfTheFile = file.cFileName;
// It could be a directory we are looking at
// if so look into that dir
if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strTheNameOfTheFile != ".") && (strTheNameOfTheFile != "..") && (bRecursive))
{
strFoundFilePath = SearchDrive(strFile, strPathToSearch + strTheNameOfTheFile, bRecursive, bStopWhenFound);
if (!strFoundFilePath.IsEmpty() && bStopWhenFound)
break;
}
}
else
{
if (strTheNameOfTheFile == strFile)
{
strFoundFilePath = strPathToSearch + strFile;
/// TODO
// ADD TO COLLECTION TYPE
if (bStopWhenFound)
break;
}
}
}
while (FindNextFile(hFile, &file));
FindClose(hFile);
}
return strFoundFilePath;
}
String strFoundFilePath = SearchDrive("file.ext", "C:\\", ...);
UPDATE: An alternative implementation of SearchDrive() that does not keep multiple search handles open while recursing through sub-directories:
#include <memory>
String Copy::SearchDrive(const String& strFile, const String& strFilePath, const bool& bRecursive, const bool& bStopWhenFound) const
{
String strFoundFilePath;
WIN32_FIND_DATA file;
String strPathToSearch = strFilePath;
if (!strPathToSearch.IsEmpty())
strPathToSearch = IncludeTrailingPathDelimiter(strPathToSearch);
HANDLE hFile = FindFirstFile((strPathToSearch + "*").c_str(), &file);
if (hFile != INVALID_HANDLE_VALUE)
{
std::auto_ptr<TStringList> subDirs;
do
{
String strTheNameOfTheFile = file.cFileName;
// It could be a directory we are looking at
// if so look into that dir
if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strTheNameOfTheFile != ".") && (strTheNameOfTheFile != "..") && (bRecursive))
{
if (subDirs.get() == NULL)
subDirs.reset(new TStringList);
subDirs->Add(strPathToSearch + strTheNameOfTheFile);
}
}
else
{
if (strTheNameOfTheFile == strFile)
{
strFoundFilePath = strPathToSearch + strFile;
/// TODO
// ADD TO COLLECTION TYPE
if (bStopWhenFound)
break;
}
}
}
while (FindNextFile(hFile, &file));
FindClose(hFile);
if (!strFoundFilePath.IsEmpty() && bStopWhenFound)
return strFoundFilePath;
if (subDirs.get() != NULL)
{
for (int i = 0; i < subDirs->Count; ++i)
{
strFoundFilePath = SearchDrive(strFile, subDirs->Strings[i], bRecursive, bStopWhenFound);
if (!strFoundFilePath.IsEmpty() && bStopWhenFound)
break;
}
}
}
return strFoundFilePath;
}
Your condition is incorrect, you should compare to to INVALID_HANDLE_VALUE
if ( hFile != INVALID_HANDLE_VALUE)
Besides you skip the first matching file returned by FindFirstFile, is that what you want?
Also I believe you need a wildcard c:\\* otherwise it will only match c:\\ itself
hFile = FindFirstFile("C:\\*", &file);