Find only file name from full path of the file in vc++ - c++

Suppose there is a CString variable which store the full path of the file.Now I hava to find only file name from if.How to do it in vc++.
CString FileName = "c:\Users\Acer\Desktop\FolderName\abc.dll";
Now I want only abc.dll.

You can use PathFindFileName.
Remember that you have to escape the \ character in your path string!

Same as already stated above, but as u are using MFC framework, this would be the way to do it. Though this does not check files existence.
CString path= "c:\\Users\\Acer\\Desktop\\FolderName\\abc.dll";
CString fileName= path.Mid(path.ReverseFind('\\')+1);

std::string str = "c:\\Users\\Acer\\Desktop\\FolderName\\abc.dll";
std::string res = str.substr( str.find_last_of("\\") + 1 );
Will get you "abs.dll".

I would use Boost::FileSystem for filename manipulation as it understands what the parts of a name would be. The function you want here would be filename()
If you are just getting the filename you can do this using CString functions. First find the ast backslash using ReverseFind and then Right to get the string wanted.

The code below demonstrate extracting a file name from a full path
#include <iostream>
#include <cstdlib>
#include <string>
#include <algorithm>
std::string get_file_name_from_full_path(const std::string& file_path)
{
std::string file_name;
std::string::const_reverse_iterator it = std::find(file_path.rbegin(), file_path.rend(), '\\');
if (it != file_path.rend())
{
file_name.assign(file_path.rbegin(), it);
std::reverse(file_name.begin(), file_name.end());
return file_name;
}
else
return file_name;
}
int main()
{
std::string file_path = "c:\\Users\\Acer\\Desktop\\FolderName\\abc.dll";
std::cout << get_file_name_from_full_path(file_path) << std::endl;
return EXIT_SUCCESS;
}

Related

I want to create a text file in cpp using ofstream

I want to create a file qbc.txt. I know how to create it, but I want to create a program that, if a file already exists with the same name, it would rename it to qbc(1).txt.
In C++17, boost's filesystem library was standardized as std::filesystem
It comes with a convenient std::filesystem::exists function.
It accepts a std::filesystem::path object, but fortunately those can be constructed with a std::string, making our program trivially easy:
std::string prefix = "qbc";
std::string extension = ".txt";
std::filesystem::path filename{prefix + extension};
int i = 0;
while (std::filesystem::exists(filename)){
filename = prefix + "(" + std::to_string(++i) + ")" + extension;
}
// now filename is "qbc(1)" or "qbc(2)" etc.
Unfortunately no compiler has full support for it at the time of this writing!
Here is a simple solution. The file_exists() function came from #Raviprakash in his response. I've added how to change the filename and try again until success. I've done an approach similar to this before in Python.
If you know that your program is the only one that will create or remove these files, then you can cache the last created one and simply create the next one instead of looping over all of the created ones every time. But this kind of optimization would only make sense if you plan to make hundreds of thousands of files this way.
#include <fstream>
#include <string>
bool file_exists(const std::string &filename) {
std::ifstream in(filename);
return in.good();
}
std::ofstream& open_new(std::ofstream &out, std::string prefix,
std::string suffix)
{
std::string filename = prefix + suffix;
unsigned int index = 0;
while (file_exists(filename)) {
index++;
filename = prefix + "(" + std::to_string(index) + ")" + suffix;
}
out.rdbuf()->open(filename, std::ios_base::out);
return out;
}
int main() {
std::string prefix = "qbc";
std::string suffix = ".txt";
std::ofstream out;
open_new(out, prefix, suffix);
out << "hello world!\n";
return 0;
}
I know the program needs some improvements but the general idea is here:
#include <fstream>
#include <string>
using namespace std;
inline bool file_exists(const std::string& name)
{
ifstream f(name.c_str());
return f.good();
}
int main()
{
string filename, name;
name = "qbc";
filename = name;
int counter = 1;
while (file_exists(filename+".txt")) {
string str = to_string(counter);
filename = name+ "(" + str + ")";
counter++;
}
filename += ".txt";
ofstream out(filename.c_str());
return 0;
}
I don't think this can be entirely solved using just the standard libraries. You can certainly keep picking a new file name until you find one that's unused and then create the new file (as the other answers have shown).
But there's an inherent race condition in that approach. What if another process creates a file between the time your program decides the name is available and the time it actually creates the file? Imagine two copies of your program both trying to write out files.
What you need is an atomic way to check for the file's existence and also to create the file. The normal way to do that is to first just try to create the file and then see if you succeeded or not. Unfortunately, I don't think the standard C++ or C libraries give you enough tools to do that. (I'd be happy to be proven wrong about that.)
Operating systems often provide APIs for doing just that. For example, Windows has GetTempFileName, which just keeps trying to create a new file until it succeeds. The key is that, once it succeeds, it keeps the file open so that it knows no other process can steal the name that's been selected.
If you tell us which OS you're using, we might be able to provide a more detailed answer.

c++ - Splitting an absolute file path

I'm writing a C++ program for a school assignment. At some point, the question requires me to change directories, which I know how to do. However, the user will provide the program with the absolute path of a file. What I'm trying to do is to change the directory to where that file is. For example, if I'm in a directory dir2, and the user want to go to the file
/home/dir1/dir2/dir3/dir4/file
I would like to do
int ret = chdir("home/dir1/dir2/dir3/dir4");
My question is how can I split the user-given string into
/home/dir1/dir2/dir3/dir4/
and
file
EDITI figured it out. I first converted the absolute pathname from a const char* to a string. Then I used the .find_last_of("/") string member to find the position of the last "/" in the string. Then I used the .substr() member to get the substring from 0 to that position returned by .find_last_of
Put your path into an std::string and then you can do something like the below.
std::string path = "/home/person/dir/file";
std::size_t botDirPos = path.find_last_of("/");
// get directory
std::string dir = path.substr(0, botDirPos);
// get file
std::string file = path.substr(botDirPos, path.length());
// change directory.
chdir(dir.c_str());
Every other answer to this question finds "/" (Unix) or "\" (Windows), and chops up the string manually; this is verbose and subject to user error. C++17 now has the std::filesystem package, which cleanly extracts directory and filename from a path in an OS friendly manner:
#include <filesystem>
void Test()
{
std::filesystem::path path("/home/dir1/dir2/dir3/dir4/file");
std::string dir = path.parent_path().string(); // "/home/dir1/dir2/dir3/dir4"
std::string file = path.filename().string(); // "file"
}
Simply get the last index of the "/" character in the file path, and snip the file with it's extension from the string.
1) Check that the directory listing has a "/". If not - throw an error.
2) Get the last index of the "/" in the string.
3) Return a sub string of the directory string, using the last index of function result (a number) as your starting index and the total length of the directory string.
Hope that helps.
you can use
std::string dir_str = "path/file";
auto pos = dir_str.rfind("/");
if (pos!= std::string::npos) {
chdir("newpath"+dir_str.substr(pos));
//...
} else {
//do something;
}
there may be issues such as character / in the file name. but assuming this is just a toy program designed for a simple test it should work.
if you are handling files somewhat seriously (like iterating through a directory recursively) I would recommend using something like boost::file_system.
You can use strtok function from <string.h> to split path components and by the way keep track of each dir in the hierarchy.
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="/path/to/file";
char * pch;
char * temp;
pch = strtok (str,"/");
while ( (temp = strtok (NULL, "/") ) != NULL)
{
pch = temp;
}
printf("The file is: %s", pch);
return 0;
}
To add to plethora of answers, I devised this after looking up stat struct and function:
struct ab_path{
int delimiter = 0;
int extension = 0;
int length = 0;
char separator = '\0';
ab_path(){}
operator bool()
{ return (this->delimiter != 0) | (this->extension != 0) | (this->length != 0) | (this->separator != '\0') ;}
};
bool ab_path( const char* name , struct ab_path* ap ){
while(1){
if(name[ap->length] == '\0'){break;}
if(name[ap->length] == '.') {ap->extension = ap->length;}
if(name[ap->length] == '/')
{ap->delimiter = ap->length; ap->separator = name[ap->length];}
if(name[ap->length] == '\\')
{ap->delimiter = ap->length;ap->separator = name[ap->length];}
++ap->length;
}
return (bool)ap;
}
struct ab_path ap;
bool valid = ap_path("superb.naming.type", &ap );
But you can rewrite an ap->delimiter to accept container of some sort (std::vector,std::array... ) and store multiple delimiters.
This might help.
What it does, it splits the file path with corresponding directories/files and store the names in a vector.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string filePath = "C:\\ProgramData\\Users\\CodeUncode\\Documents";
vector<string> directories;
size_t position=0, currentPosition=0;
while(currentPosition != -1)
{
currentPosition = filePath.find_first_of('\\', position);
directories.push_back(filePath.substr(position,currentPosition-position));
position = currentPosition+1;
}
for(vector<string>::iterator it = directories.begin(); it!=directories.end(); it++)
cout<<*it<<endl;
return 0;
}

C++ string equivalent for strrchr

Using C strings I would write the following code to get the file name from a file path:
#include <string.h>
const char* filePath = "dir1\\dir2\\filename"; // example
// extract file name (including extension)
const char* fileName = strrchr(progPath, '\\');
if (fileName)
++fileName;
else
fileName = filePath;
How to do the same with C++ strings? (i.e. using std::string from #include <string>)
The closest equivalent is rfind:
#include <string>
std::string filePath = "dir1\\dir2\\filename"; // example
// extract file name (including extension)
std::string::size_type filePos = filePath.rfind('\\');
if (filePos != std::string::npos)
++filePos;
else
filePos = 0;
std::string fileName = filePath.substr(filePos);
Note that rfind returns an index into the string (or npos), not a pointer.
To find the last occurence of a symbol in a string use std::string::rfind
std::string filename = "dir1\\dir2\\filename";
std::size_t pos = filename.rfind( "\\" );
However, if you're handling filenames and pathes more often, have a look at boost::filesystem
boost::filesystem::path p("dir1\\dir2\\filename");
std::string filename = p.filename().generic_string(); //or maybe p.filename().native();
Either call string::rfind(), or call std::find using reverse iterators (which are returned from string::rbegin() and string::rend()).
find might be a little bit more efficient since it explicitly says that you're looking for a matching character. rfind() looks for a substring and you'd give it a length 1 string, so it finds the same thing.
Apart from rfind(), you can also use find_last_of()
You have an example too written in cplusplus.com which is same as your requirement.

C++ write to file error with full PATH from GetEnvironmentVariable()

I'm noob in C++ but wanting to learn. I have a little program that writes some info to my \etc\hosts in Windows; I get the %WINDIR% variable via GetEnvironmentVariable(), if I put the full path manually everything is ok, but when I substitute with WINDIR variable my code isn't compiling. I know I don't do something right.
#include <windows.h>
#include <ios>
#include <fstream>
char buffer[1000];
int main() {
GetEnvironmentVariable("WINDIR",(char*)&buffer,sizeof(buffer));
std::ofstream log;
log.open("%s\\system32\\drivers\\etc\\hosts", buffer);
log << "127.0.0.1 domain.com\n" << std::endl;
return 0;
}
I get really ugly errors like:
C:\Documents and Settings\xtmtrx\Desktop\coding\windir.cpp no matching function for call to `std::basic_ofstream<char, std::char_traits<char> >::open(const char[30], char[1000])'
ofstream cannot format the path for you. You need to do that separately, eg:
#include <windows.h>
#include <ios>
#include <fstream>
char buffer[1000] = {0};
int main() {
GetEnvironmentVariable("WINDIR",buffer,sizeof(buffer));
strcat(buffer, "\\system32\\drivers\\etc\\hosts");
std::ofstream log;
log.open(buffer, ios_base::ate);
log << "127.0.0.1 domain.com\n" << std::endl;
return 0;
}
FYI, you should use GetWindowsDirectory(), GetSystemDirectory(), SHGetSpecialFolderPath() or SHGetKnownFolderPath() instead of GetEnvironmentVariable(). And you should use PathCombine() when concantenating paths together so it can ensure the slashes are correct.
open("%s\\system32\\drivers\\etc\\hosts", buffer); open doesn't understand format strings..you are using %s does not make sense. learn here
Try like this:
GetEnvironmentVariable("WINDIR",buffer,sizeof(buffer));
strcat(buffer, "\\system32\\drivers\\etc\\hosts");
std::ofstream log;
log.open(buffer.str().c_str(), ios_base::ate);
You need to concate the string together like this:
LPTSTR windir[MAX_PATH];
LPTSTR fullpath[MAX_PATH];
GetWindowsDirectory(windir, MAX_PATH);
if(PathCombine(fullpath, windir, _T("system32\\drivers\\etc\\hosts")) != NULL) {
std::ofstream log;
log.open(buffer, ios_base::ate);
log << "127.0.0.1 domain.com\n" << std::endl;
}
At first you need to concate the directory and the file part with PathCombine. Then you can open the file and write the content. You should also note that you need admin permissions to change this file and some antivirus programmes may reject the access of the hosts file.

get directory from file path c++

What is the simplest way to get the directory that a file is in? I'm using this to find the working directory.
string filename = "C:\MyDirectory\MyFile.bat"
In this example, I should get "C:\MyDirectory".
The initialisation is incorrect as you need to escape the backslashes:
string filename = "C:\\MyDirectory\\MyFile.bat";
To extract the directory if present:
string directory;
const size_t last_slash_idx = filename.rfind('\\');
if (std::string::npos != last_slash_idx)
{
directory = filename.substr(0, last_slash_idx);
}
The quick and dirty:
Note that you must also look for / because it is allowed alternative path separator on Windows
#include <string>
#include <iostream>
std::string dirnameOf(const std::string& fname)
{
size_t pos = fname.find_last_of("\\/");
return (std::string::npos == pos)
? ""
: fname.substr(0, pos);
}
int main(int argc, const char *argv[])
{
const std::string fname = "C:\\MyDirectory\\MyFile.bat";
std::cout << dirnameOf(fname) << std::endl;
}
Use the Boost.filesystem parent_path() function.
Ex. argument c:/foo/bar => c:/foo
More examples here : path decomposition table and tutorial here.
C++17 provides std::filesystem::path. It may be available in C++11 in ; link with -lstdc++fs. Note the function does not validate the path exists; use std::filesystem::status to determine type of file (which may be filetype::notfound)
The MFC way;
#include <afx.h>
CString GetContainingFolder(CString &file)
{
CFileFind fileFind;
fileFind.FindFile(file);
fileFind.FindNextFile();
return fileFind.GetRoot();
}
or, even simpler
CString path(L"C:\\my\\path\\document.txt");
path.Truncate(path.ReverseFind('\\'));
As Question is old but I would like to add an answer so that it will helpful for others.
in Visual c++ you can use CString or char array also
CString filename = _T("C:\\MyDirectory\\MyFile.bat");
PathRemoveFileSpec(filename);
OUTPUT:
C:\MyDirectory
Include Shlwapi.h in your header files.
MSDN LINK here you can check example.
A very simple cross-platform solution (as adapted from this example for string::find_last_of):
std::string GetDirectory (const std::string& path)
{
size_t found = path.find_last_of("/\\");
return(path.substr(0, found));
}
This works for both cases where the slashes can be either backward or forward pointing (or mixed), since it merely looks for the last occurrence of either in the string path.
However, my personal preference is using the Boost::Filesystem libraries to handle operations like this. An example:
std::string GetDirectory (const std::string& path)
{
boost::filesystem::path p(path);
return(p.parent_path().string());
}
Although, if getting the directory path from a string is the only functionality you need, then Boost might be a bit overkill (especially since Boost::Filesystem is one of the few Boost libraries that aren't header-only). However, AFIK, Boost::Filesystem had been approved to be included into the TR2 standard, but might not be fully available until the C++14 or C++17 standard (likely the latter, based on this answer), so depending on your compiler (and when you're reading this), you may not even need to compile these separately anymore since they might be included with your system already. For example, Visual Studio 2012 can already use some of the TR2 filesystem components (according to this post), though I haven't tried it since I'm still using Visual Studio 2010...
You can use the _spliltpath function available in stdlib.h header. Please refer to this link for the same.
http://msdn.microsoft.com/en-us/library/aa273364%28v=VS.60%29.aspx
Since C++17 you can use std::filesystem::parent_path:
#include <filesystem>
#include <iostream>
int main() {
std::string filename = "C:\\MyDirectory\\MyFile.bat";
std::string directory = std::filesystem::path(filename).parent_path().u8string();
std::cout << directory << std::endl;
}
This is proper winapi solution:
CString csFolder = _T("c:\temp\file.ext");
PathRemoveFileSpec(csFolder.GetBuffer(0));
csFolder.ReleaseBuffer(-1);
If you have access to Qt, you can also do it like this:
std::string getDirectory(const std::string & file_path)
{
return QFileInfo(QString(file_path)).absolutePath().toStdString();
}
The way of Beetle)
#include<tchar.h>
int GetDir(TCHAR *fullPath, TCHAR *dir) {
const int buffSize = 1024;
TCHAR buff[buffSize] = {0};
int buffCounter = 0;
int dirSymbolCounter = 0;
for (int i = 0; i < _tcslen(fullPath); i++) {
if (fullPath[i] != L'\\') {
if (buffCounter < buffSize) buff[buffCounter++] = fullPath[i];
else return -1;
} else {
for (int i2 = 0; i2 < buffCounter; i2++) {
dir[dirSymbolCounter++] = buff[i2];
buff[i2] = 0;
}
dir[dirSymbolCounter++] = fullPath[i];
buffCounter = 0;
}
}
return dirSymbolCounter;
}
Using :
TCHAR *path = L"C:\\Windows\\System32\\cmd.exe";
TCHAR dir[1024] = {0};
GetDir(path, dir);
wprintf(L"%s\n%s\n", path, dir);
You can simply search the last "\" and then cut the string:
string filePath = "C:\MyDirectory\MyFile.bat"
size_t slash = filePath.find_last_of("\");
string dirPath = (slash != std::string::npos) ? filePath.substr(0, slash) : filePath;
make sure in Linux to search "/" instead of "\":
size_t slash = filePath.find_last_of("/");