How to get parent folder from path in C++ - c++

I am using C++ in linux and I want to extract the parent folder from a path in C++ 14.
For example, I have the path likes
/home/abc/fi.mp4
My expected output be abc. How can I do it in C++
This is that I did
std::string getFolderName(const std::string &path) {
size_t found = path.find_last_of("/\\");
string foldername = ...
return foldername;
}

I believe the experimental version of std::filesystem was already available in C++14.
If it is indeed available in your environment, you can use parent_path() to get the parent folder from a path. Then you can use the method filename() to extract the name without the full path specification.
In order to convert a path to std::string you can use the string() method.
#include <string>
#include <iostream>
#include <experimental/filesystem>
int main(int argc, char const* argv[])
{
std::string path_str = "/home/abc/fi.mp4";
std::experimental::filesystem::path p = path_str;
std::experimental::filesystem::path parent_p = p.parent_path();
std::experimental::filesystem::path parent_p_fname = p.parent_path().filename();
std::string result_str = parent_p_fname.string();
std::cout << result_str << std::endl;
return 0;
}
Output:
abc
You can further use parent_path() to climb up the hirarchy.
Once C++17 is available for you, you can simply drop the experimental prefix.

You probably want something like this:
// Returns the path-string without the filename part
std::string getFolderPath(const std::string &path) {
const size_t found = path.find_last_of("/\\");
return (found == string::npos) ? path : path.substr(0, found);
}
// Returns only the filename part (after the last slash)
std::string getFileName(const std::string & path) {
const size_t found = path.find_last_of("/\\");
return (found == string::npos) ? path : path.substr(found+1);
}

The function getFolders will get you all the folder names in a path.
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
vector<string> getFolders(const char* path) {
vector<string> folders;
stringstream ss;
int i = 0, k = 0;
char c;
while ((c = *(path + i)) != '\0') {
if (c != '/'){
ss << c;
++k;
}
else {
if (k > 0){
folders.push_back(ss.str());
ss.str(string());
k = 0;
}
}
++i;
}
return folders;
}
void main()
{
vector<string> folders = getFolders("/home/abc/fi.mp4");
for (auto f : folders){
cout << f << endl;
}
}

Related

how could I create a file in this path : "~/testUsr" by using boost

I want to create a file in the path "~/testUsr" while I don't know which "testUsr" exactly is. if I just use path "~/testUsr", it will generate a directory "~" in current path instead of "home/testUsr";
// create directories
std::string directoryPath = "~/testUsr";
if(!boost::filesystem::exists(directoryPath)){
boost::filesystem::create_directories(boost::filesystem::path(directoryPath));
}
std::string filePath = directoryPath + "/" + filename +".ini";
if(!boost::filesystem::exists(filePath)){
boost::filesystem::ofstream File(filePath) ;
config.WriteConfigFile(filePath);
File.close();
}
Using a helper function from Getting absolute path in boost
Live On Coliru
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <iostream>
using boost::filesystem::path;
struct {
void WriteConfigFile(std::ostream& os) const {
os << "[section]\nkey=value\n";
}
} static config;
path expand(path p) {
char const* const home = getenv("HOME");
if (home == nullptr)
return p; // TODO handle as error?
auto s = p.generic_string<std::string>();
if (!s.empty() && s.find("~/") == 0u) {
return home + s.substr(1);
}
return p;
}
int main() {
path filename = "abc";
// create directories
path directoryPath = expand("~/testUsr");
if (!exists(directoryPath)) {
create_directories(directoryPath);
}
path filePath = directoryPath / filename.replace_extension(".ini");
if (!exists(filePath)) {
boost::filesystem::ofstream os(filePath);
config.WriteConfigFile(os);
} // os.close(); implied by destructor
}
Note the use of path instead of string to make the code more reliable.

Recursive function for listing all files in sub directories

I'm trying to write a function that returns a list of all files on current folder and all of its sub folders. I wrote this code:
#include <iostream>
#include <dirent.h>
#include <cstring>
using namespace std;
int main() {
DIR* dir; dirent* pdir;
//From my workspace
dir=opendir(".");
while (pdir=readdir(dir)) {
if(/**********This pdir is a directory**********/) {
/**********RECURSIVE CALL SHOULD BE HERE**********/
cout<<pdir->d_name<<endl;
}
}
closedir(dir);
return 0;
}
I searched for it in google and I don't know how to:
Check if the current pdir is directory
Go inside the directory and perform the recursive call on it
Meanwhile I have everything on main because I still don't know what arguments the recursive function should have.
Any hints?
Here is a version using proposed standard filesystem library:
#include <iostream>
#include <filesystem>
using namespace std;
using namespace std::tr2::sys;
void main()
{
for (recursive_directory_iterator i("."), end; i != end; ++i)
if (!is_directory(i->path()))
cout << i->path().filename() << "\n";
}
My approach in C++11:
#include <string>
#include <functional>
#include <dirent.h>
void listFiles(const std::string &path, std::function<void(const std::string &)> cb) {
if (auto dir = opendir(path.c_str())) {
while (auto f = readdir(dir)) {
if (!f->d_name || f->d_name[0] == '.') continue;
if (f->d_type == DT_DIR)
listFiles(path + f->d_name + "/", cb);
if (f->d_type == DT_REG)
cb(path + f->d_name);
}
closedir(dir);
}
}
Usage:
listFiles("my_directory/", [](const std::string &path) {
std::cout << path << std::endl;
});
Unless your goal is to learn how to write a recursive function, you might prefer this simple loop based on Boost.Filesystem:
#include "boost/filesystem.hpp"
#include <iostream>
int main () {
for ( boost::filesystem::recursive_directory_iterator end, dir("./");
dir != end; ++dir ) {
// std::cout << *dir << "\n"; // full path
std::cout << dir->path().filename() << "\n"; // just last bit
}
}
Or even the single function call:
std::copy(
boost::filesystem::recursive_directory_iterator("./"),
boost::filesystem::recursive_directory_iterator(),
std::ostream_iterator<boost::filesystem::directory_entry>(std::cout, "\n"));
Isolate that code in a procedure that takes the base directory path as a parameter, so you can actually perform the recursive call. It should be something like
void recursive_file_list(const char * directory)
{
// ...
}
Then, to check if the pdir you obtained is a directory, you have two routes:
you can check if pdir->d_type==DT_DIR; this gives you this information immediately, but it's not portable (POSIX does not mandate the existence of the d_type member); also, it's not supported for all the filesystems, so you may get DT_UNKNOWN. If you want to follow symlinks, you have to perform extra checks also if you get DT_LNK. In these cases, you must fall back to lstat (see the point below);
you can instead portably use lstat to get information about each file, checking in particular the st_mode field of struct stat.
Using C++17 recursive_directory_iterator it becomes as concise as:
void ls_recursive(const std::filesystem::path& path) {
for(const auto& p: std::filesystem::recursive_directory_iterator(path)) {
if (!std::filesystem::is_directory(p)) {
std::cout << p.path() << '\n';
}
}
}
With example output:
"/home/user/prj/rust/stack/Cargo.toml"
"/home/user/prj/rust/stack/.gitignore"
"/home/user/prj/rust/stack/src/main.rs"
"/home/user/prj/rust/stack/.git/config"
It uses standard c++ functionality. No need to include any third party library in code.
Only send directory path as parameter. It will revert you every files path present in that folder and its sub folder.
Further that, if you need to sort any specific type file (i.e. .txt or .jpg), pass extension, it will print all the files path which having respective extension.
#include <Windows.h>
#include<iostream>
#include<vector>
#include<string>
using namespace std;
vector<string> files;
std::string Recursive(std::string folder) {
std::string search_path = folder + "/*.*";
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
std::string tmp;
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))) {
tmp = folder + "\\";
tmp = tmp + fd.cFileName;
Recursive(tmp);
}
}
else {
std::string FinalFilePath = folder + "\\" + fd.cFileName;
files.push_back(FinalFilePath);
}
} while (::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return folder;
}
bool has_suffix(const std::string& str, const std::string& suffix) {
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
int main(){
std::string folder = "C:\\Users\\Omkar\\Desktop\\Test";
Recursive(folder);
std::string t;
const auto needle = std::string(".txt");
while (!files.empty()) {
t = files.back();
if (has_suffix(t, ".mmt")) {
cout << "FINAL PATH : " << t << endl;
t.clear();
}
files.pop_back();
}
return 0;
}
Path should look like /your_path/. For search inside hidden folders you should add third parameter true.
#include <dirent.h>
#include <vector>
#include <cstring>
void GetReqDirs(const std::string& path, std::vector<string>& files,const bool showHiddenDirs = false){
DIR *dpdf;
struct dirent *epdf;
dpdf = opendir(path.c_str());
if (dpdf != NULL){
while ((epdf = readdir(dpdf)) != NULL){
if(showHiddenDirs ? (epdf->d_type==DT_DIR && string(epdf->d_name) != ".." && string(epdf->d_name) != "." ) : (epdf->d_type==DT_DIR && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ) ){
GetReqDirs(path+epdf->d_name+"/",files, showHiddenDirs);
}
if(epdf->d_type==DT_REG){
files.push_back(path+epdf->d_name);
}
}
}
closedir(dpdf);
}
You could check if there is no "." in the string.
if(strstr(pdir->d_name,".") != NULL)
Here how I use it
std::vector<std::string> get_all_files_recursive(const fs::path& path)
{
std::vector<std::string> result;
for (const auto& p : fs::recursive_directory_iterator(path))
{
if (!fs::is_directory(p))
{
fs::path path = p.path();
result.push_back(path.u8string());
}
}
return result;
}
Great stuff! Here is my Windows friendly solution using std only since i had few bumps with the above solutions on Windows specifically, works in DLLs as well:
#include <windows.h> // messagebox
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <filesystem> // C++17 standard header file name
#include <experimental/filesystem> // Header file for pre-standard implementation
using namespace std::experimental::filesystem::v1;
for (recursive_directory_iterator next(path(dir.c_str())), end; next != end; ++next)
{
auto path = next->path();
if (path.has_extension())
{
MessageBox(0, path.wstring().c_str(), L"Filepath", 0);
}
}

Get a file name from a path

What is the simplest way to get the file name that from a path?
string filename = "C:\\MyDirectory\\MyFile.bat"
In this example, I should get "MyFile". without extension.
The task is fairly simple as the base filename is just the part of the string starting at the last delimeter for folders:
std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
If the extension is to be removed as well the only thing to do is find the last . and take a substr to this point
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
Perhaps there should be a check to cope with files solely consisting of extensions (ie .bashrc...)
If you split this up into seperate functions you're flexible to reuse the single tasks:
template<class T>
T base_name(T const & path, T const & delims = "/\\")
{
return path.substr(path.find_last_of(delims) + 1);
}
template<class T>
T remove_extension(T const & filename)
{
typename T::size_type const p(filename.find_last_of('.'));
return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
}
The code is templated to be able to use it with different std::basic_string instances (i.e. std::string & std::wstring...)
The downside of the templation is the requirement to specify the template parameter if a const char * is passed to the functions.
So you could either:
A) Use only std::string instead of templating the code
std::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\\") + 1);
}
B) Provide wrapping function using std::string (as intermediates which will likely be inlined / optimized away)
inline std::string string_base_name(std::string const & path)
{
return base_name(path);
}
C) Specify the template parameter when calling with const char *.
std::string base = base_name<std::string>("some/path/file.ext");
Result
std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;
Prints
MyFile
A possible solution:
string filename = "C:\\MyDirectory\\MyFile.bat";
// Remove directory if present.
// Do this before extension removal incase directory has a period character.
const size_t last_slash_idx = filename.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
filename.erase(0, last_slash_idx + 1);
}
// Remove extension if present.
const size_t period_idx = filename.rfind('.');
if (std::string::npos != period_idx)
{
filename.erase(period_idx);
}
The Simplest way in C++17 is:
use the #include <filesystem> and filename() for filename with extension and stem() without extension.
#include <iostream>
#include <string>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
std::string filename = "C:\\MyDirectory\\MyFile.bat";
std::cout << fs::path(filename).filename() << '\n'
<< fs::path(filename).stem() << '\n'
<< fs::path("/foo/bar.txt").filename() << '\n'
<< fs::path("/foo/bar.txt").stem() << '\n'
<< fs::path("/foo/.bar").filename() << '\n'
<< fs::path("/foo/bar/").filename() << '\n'
<< fs::path("/foo/.").filename() << '\n'
<< fs::path("/foo/..").filename() << '\n'
<< fs::path(".").filename() << '\n'
<< fs::path("..").filename() << '\n'
<< fs::path("/").filename() << '\n';
}
Which can be compiled with g++ -std=c++17 main.cpp -lstdc++fs, and outputs:
"MyFile.bat"
"MyFile"
"bar.txt"
"bar"
".bar"
""
"."
".."
"."
".."
"/"
Reference: cppreference
The simplest solution is to use something like boost::filesystem. If
for some reason this isn't an option...
Doing this correctly will require some system dependent code: under
Windows, either '\\' or '/' can be a path separator; under Unix,
only '/' works, and under other systems, who knows. The obvious
solution would be something like:
std::string
basename( std::string const& pathname )
{
return std::string(
std::find_if( pathname.rbegin(), pathname.rend(),
MatchPathSeparator() ).base(),
pathname.end() );
}
, with MatchPathSeparator being defined in a system dependent header
as either:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '/';
}
};
for Unix, or:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '\\' || ch == '/';
}
};
for Windows (or something still different for some other unknown
system).
EDIT: I missed the fact that he also wanted to suppress the extention.
For that, more of the same:
std::string
removeExtension( std::string const& filename )
{
std::string::const_reverse_iterator
pivot
= std::find( filename.rbegin(), filename.rend(), '.' );
return pivot == filename.rend()
? filename
: std::string( filename.begin(), pivot.base() - 1 );
}
The code is a little bit more complex, because in this case, the base of
the reverse iterator is on the wrong side of where we want to cut.
(Remember that the base of a reverse iterator is one behind the
character the iterator points to.) And even this is a little dubious: I
don't like the fact that it can return an empty string, for example.
(If the only '.' is the first character of the filename, I'd argue
that you should return the full filename. This would require a little
bit of extra code to catch the special case.)
}
_splitpath should do what you need. You could of course do it manually but _splitpath handles all special cases as well.
EDIT:
As BillHoag mentioned it is recommended to use the more safe version of _splitpath called _splitpath_s when available.
Or if you want something portable you could just do something like this
std::vector<std::string> splitpath(
const std::string& str
, const std::set<char> delimiters)
{
std::vector<std::string> result;
char const* pch = str.c_str();
char const* start = pch;
for(; *pch; ++pch)
{
if (delimiters.find(*pch) != delimiters.end())
{
if (start != pch)
{
std::string str(start, pch);
result.push_back(str);
}
else
{
result.push_back("");
}
start = pch + 1;
}
}
result.push_back(start);
return result;
}
...
std::set<char> delims{'\\'};
std::vector<std::string> path = splitpath("C:\\MyDirectory\\MyFile.bat", delims);
cout << path.back() << endl;
If you can use boost,
#include <boost/filesystem.hpp>
boost::filesystem::path p("C:\\MyDirectory\\MyFile.bat");
string basename = p.filename().string();
//or
//string basename = boost::filesystem::path("C:\\MyDirectory\\MyFile.bat").filename().string();
This is all.
I recommend you to use boost library. Boost gives you a lot of conveniences when you work with C++. It supports almost all platforms.
If you use Ubuntu, you can install boost library by only one line sudo apt-get install libboost-all-dev (ref. How to install Boost on Ubuntu)
You can also use the shell Path APIs PathFindFileName, PathRemoveExtension. Probably worse than _splitpath for this particular problem, but those APIs are very useful for all kinds of path parsing jobs and they take UNC paths, forward slashes and other weird stuff into account.
wstring filename = L"C:\\MyDirectory\\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart);
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx
The drawback is that you have to link to shlwapi.lib, but I'm not really sure why that's a drawback.
Function:
#include <string>
std::string
basename(const std::string &filename)
{
if (filename.empty()) {
return {};
}
auto len = filename.length();
auto index = filename.find_last_of("/\\");
if (index == std::string::npos) {
return filename;
}
if (index + 1 >= len) {
len--;
index = filename.substr(0, len).find_last_of("/\\");
if (len == 0) {
return filename;
}
if (index == 0) {
return filename.substr(1, len - 1);
}
if (index == std::string::npos) {
return filename.substr(0, len);
}
return filename.substr(index + 1, len - index - 1);
}
return filename.substr(index + 1, len - index);
}
Tests:
#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>
TEST_CASE("basename")
{
CHECK(basename("") == "");
CHECK(basename("no_path") == "no_path");
CHECK(basename("with.ext") == "with.ext");
CHECK(basename("/no_filename/") == "no_filename");
CHECK(basename("no_filename/") == "no_filename");
CHECK(basename("/no/filename/") == "filename");
CHECK(basename("/absolute/file.ext") == "file.ext");
CHECK(basename("../relative/file.ext") == "file.ext");
CHECK(basename("/") == "/");
CHECK(basename("c:\\windows\\path.ext") == "path.ext");
CHECK(basename("c:\\windows\\no_filename\\") == "no_filename");
}
From C++ Docs - string::find_last_of
#include <iostream> // std::cout
#include <string> // std::string
void SplitFilename (const std::string& str) {
std::cout << "Splitting: " << str << '\n';
unsigned found = str.find_last_of("/\\");
std::cout << " path: " << str.substr(0,found) << '\n';
std::cout << " file: " << str.substr(found+1) << '\n';
}
int main () {
std::string str1 ("/usr/bin/man");
std::string str2 ("c:\\windows\\winhelp.exe");
SplitFilename (str1);
SplitFilename (str2);
return 0;
}
Outputs:
Splitting: /usr/bin/man
path: /usr/bin
file: man
Splitting: c:\windows\winhelp.exe
path: c:\windows
file: winhelp.exe
C++11 variant (inspired by James Kanze's version) with uniform initialization and anonymous inline lambda.
std::string basename(const std::string& pathname)
{
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; }).base(),
pathname.end()};
}
It does not remove the file extension though.
The boost filesystem library is also available as the experimental/filesystem library and was merged into ISO C++ for C++17. You can use it like this:
#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
int main () {
std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}
Output:
"bar.txt"
It also works for std::string objects.
this is the only thing that actually finally worked for me:
#include "Shlwapi.h"
CString some_string = "c:\\path\\hello.txt";
LPCSTR file_path = some_string.GetString();
LPCSTR filepart_c = PathFindFileName(file_path);
LPSTR filepart = LPSTR(filepart_c);
PathRemoveExtension(filepart);
pretty much what Skrymsli suggested but doesn't work with wchar_t*,
VS Enterprise 2015
_splitpath worked as well, but I don't like having to guess at how many char[?] characters I'm going to need; some people probably need this control, i guess.
CString c_model_name = "c:\\path\\hello.txt";
char drive[200];
char dir[200];
char name[200];
char ext[200];
_splitpath(c_model_name, drive, dir, name, ext);
I don't believe any includes were needed for _splitpath. No external libraries (like boost) were needed for either of these solutions.
std::string getfilename(std::string path)
{
path = path.substr(path.find_last_of("/\\") + 1);
size_t dot_i = path.find_last_of('.');
return path.substr(0, dot_i);
}
I would do it by...
Search backwards from the end of the string until you find the first backslash/forward slash.
Then search backwards again from the end of the string until you find the first dot (.)
You then have the start and end of the file name.
Simples...
You can use the std::filesystem to do it quite nicely:
#include <filesystem>
namespace fs = std::experimental::filesystem;
fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
m_szFilePath.MakeLower();
CFileFind finder;
DWORD buffSize = MAX_PATH;
char longPath[MAX_PATH];
DWORD result = GetLongPathName(m_szFilePath, longPath, MAX_PATH );
if( result == 0)
{
m_bExists = FALSE;
return;
}
m_szFilePath = CString(longPath);
m_szFilePath.Replace("/","\\");
m_szFilePath.Trim();
//check if it does not ends in \ => remove it
int length = m_szFilePath.GetLength();
if( length > 0 && m_szFilePath[length - 1] == '\\' )
{
m_szFilePath.Truncate( length - 1 );
}
BOOL bWorking = finder.FindFile(this->m_szFilePath);
if(bWorking){
bWorking = finder.FindNextFile();
finder.GetCreationTime(this->m_CreationTime);
m_szFilePath = finder.GetFilePath();
m_szFileName = finder.GetFileName();
this->m_szFileExtension = this->GetExtension( m_szFileName );
m_szFileTitle = finder.GetFileTitle();
m_szFileURL = finder.GetFileURL();
finder.GetLastAccessTime(this->m_LastAccesTime);
finder.GetLastWriteTime(this->m_LastWriteTime);
m_ulFileSize = static_cast<unsigned long>(finder.GetLength());
m_szRootDirectory = finder.GetRoot();
m_bIsArchive = finder.IsArchived();
m_bIsCompressed = finder.IsCompressed();
m_bIsDirectory = finder.IsDirectory();
m_bIsHidden = finder.IsHidden();
m_bIsNormal = finder.IsNormal();
m_bIsReadOnly = finder.IsReadOnly();
m_bIsSystem = finder.IsSystem();
m_bIsTemporary = finder.IsTemporary();
m_bExists = TRUE;
finder.Close();
}else{
m_bExists = FALSE;
}
The variable m_szFileName contains the fileName.
Dont use _splitpath() and _wsplitpath(). They are not safe, and they are obsolete!
Instead, use their safe versions, namely _splitpath_s() and _wsplitpath_s()
This should work too :
// strPath = "C:\\Dir\\File.bat" for example
std::string getFileName(const std::string& strPath)
{
size_t iLastSeparator = 0;
return strPath.substr((iLastSeparator = strPath.find_last_of("\\")) != std::string::npos ? iLastSeparator + 1 : 0, strPath.size() - strPath.find_last_of("."));
}
If you can use it, Qt provide QString (with split, trim etc), QFile, QPath, QFileInfo etc to manipulate files, filenames and directories. And of course it's also cross plaftorm.
A really simple and short function that returns the filename+path that I made which uses no dependencies:
const char* GetFileNameFromPath(const char* _buffer)
{
char c;
int i;
for (i = 0; ;++i) {
c = *((char*)_buffer+i);
if (c == '\\' || c == '/')
return GetFileNameFromPath((char*)_buffer + i + 1);
if (c == '\0')
return _buffer;
}
return "";
}
To only get the filename without the extension you could change c == '\0' to c == '.'.
For long time I was looking for a function able to properly decompose file path. For me this code is working perfectly for both Linux and Windows.
void decomposePath(const char *filePath, char *fileDir, char *fileName, char *fileExt)
{
#if defined _WIN32
const char *lastSeparator = strrchr(filePath, '\\');
#else
const char *lastSeparator = strrchr(filePath, '/');
#endif
const char *lastDot = strrchr(filePath, '.');
const char *endOfPath = filePath + strlen(filePath);
const char *startOfName = lastSeparator ? lastSeparator + 1 : filePath;
const char *startOfExt = lastDot > startOfName ? lastDot : endOfPath;
if(fileDir)
_snprintf(fileDir, MAX_PATH, "%.*s", startOfName - filePath, filePath);
if(fileName)
_snprintf(fileName, MAX_PATH, "%.*s", startOfExt - startOfName, startOfName);
if(fileExt)
_snprintf(fileExt, MAX_PATH, "%s", startOfExt);
}
Example results are:
[]
fileDir: ''
fileName: ''
fileExt: ''
[.htaccess]
fileDir: ''
fileName: '.htaccess'
fileExt: ''
[a.exe]
fileDir: ''
fileName: 'a'
fileExt: '.exe'
[a\b.c]
fileDir: 'a\'
fileName: 'b'
fileExt: '.c'
[git-archive]
fileDir: ''
fileName: 'git-archive'
fileExt: ''
[git-archive.exe]
fileDir: ''
fileName: 'git-archive'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git-core\.htaccess]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: '.htaccess'
fileExt: ''
[D:\Git\mingw64\libexec\git-core\a.exe]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: 'a'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git-core\git-archive.exe]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: 'git-archive'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git.core\git-archive.exe]
fileDir: 'D:\Git\mingw64\libexec\git.core\'
fileName: 'git-archive'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git-core\git-archiveexe]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: 'git-archiveexe'
fileExt: ''
[D:\Git\mingw64\libexec\git.core\git-archiveexe]
fileDir: 'D:\Git\mingw64\libexec\git.core\'
fileName: 'git-archiveexe'
fileExt: ''
I hope this helps you also :)
shlwapi.lib/dll uses the HKCU registry hive internally.
It's best not to link to shlwapi.lib if you're creating a library or the product does not have a UI. If you're writing a lib then your code can be used in any project including those that don't have UIs.
If you're writing code that runs when a user is not logged in (e.g. service [or other] set to start at boot or startup) then there's no HKCU. Lastly, shlwapi are settlement functions; and as a result high on the list to deprecate in later versions of Windows.
A slow but straight forward regex solution:
std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
I implemented a function that might meet your needs.
It is based on string_view's constexpr function find_last_of (since c++17) which can be calculated at compile time
constexpr const char* base_filename(const char* p) {
const size_t i = std::string_view(p).find_last_of('/');
return std::string_view::npos == i ? p : p + i + 1 ;
}
//in the file you used this function
base_filename(__FILE__);
Here is the simplest version:
#include <iostream>
#include <string>
int main()
{
std::string filepath = "directory/file-name.txt";
std::string filename = filepath.substr(filepath.find_last_of("/")+1, filepath.find_last_of(".") - filepath.find_last_of("/") - 1);
std::cout << filename << std::endl;
}
Returns:
file-name

Function to parse string with tokens

I know how to program in C# and VB but not have idea about how to use C++ and have to program a little exe to a barcode scanner that use C++ :(
In this moment I try to parse a scanned barcode that have multiple data sepparated with a "/", I find that exist a strtok function, tested it "manually" and worked ok but I not implemented yet a working function to call it correctly, what I have now:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int elemStr(char *str, char sep)
{
int cantElem;
unsigned ich;
cantElem = 0;
if (strlen(str) > 0) //at least 1 elem
cantElem++;
for (ich = 0; ich < strlen(str); ich++)
{
if (str[ich] == sep)
cantElem++;
}
return cantElem;
}
char* getElemStr(char *str, char sep[], int elem)
{
char *tempStr = NULL;
char *tok;
int currElem = 1;
// 1st data
strcpy( tempStr, str);
tok = strtok( tempStr, sep);
while( currElem != elem )
{
// Get next tokens:
tok = strtok( NULL, sep );
currElem++;
}
return tok;
}
void main( void )
{
char barcode[] = "710015733801Z/1/35";
char sep[] = "/";
char sep1 = sep[0];
char barcd[20];
char piezaChar[4];
int pieza;
char mtsChar[4];
int cantElem;
cantElem = elemStr(barcode, sep1 );
if (cantElem >= 1)
{
strcpy(barcd, getElemStr(barcode,sep,1) ); //pasa a str resultado;
printf("Cod: %s\n", barcd ); //STACK OVERFLOW HERE!
}
}
if I use strtok witout a function "getElemStr" it work ok but I try to use it on other places too.
Can I use strtok like this? You have a working example?
pd: I not have idea about pointers (sorry), good doc to learn about that?
Since you specifically asked about C++, I'm going to ignore your very c-style code and show you how to do this in C++:
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::string barcode = "710015733801Z/1/35";
std::string sep = "/";
std::vector<std::string> v;
boost::split(v, barcode, boost::is_any_of(sep));
for(size_t i=0; i<v.size(); ++i)
std::cout << v[i] << '\n';
}
strtok destroys your original string. So i don't think it can be used with a char* that points to a static string. Static strings get copied to a read only portion of the executable.
Here is a C++ solution that doesn't use boost.
#include <string>
#include <sstream>
#include <iostream>
int main()
{
std::string barcode = "710015733801Z/1/35";
std::stringstream ss(barcode);
std::string elem;
while(std::getline(ss, elem, '/'))
{
//do something else meaningful with elem
std::cout << elem << std::endl;
}
return 0;
}
Output:
710015733801Z
1
35

How do you use string.erase and string.find?

Why cant i call string.find in string.erase like so: str.erase(str.find(a[1]),str.size())?
edit:code added
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// html tags
string tags[5]={"<!--...-->","<!DOCTYPE>","<a>","<abbr>","<acronym>"};
//
//check if string exists
int boolStringExists(string a, string b)
{
if(a.find(b)>0)
{
return 1;
}
if(a.find(b)<=0)
{
return 0;
}
}
//erase tag from string a
void eraseTags(string a,string b[])
{
for(int i=0; i<5;i++)
{
int x=(boolStringExists(a,b[i]));
while (x>0)
{
a.erase(a.find(b[i]),b[i].size());
x=(boolStringExists(a,b[i]));
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
fstream file;
file.open("h:\\a.htm");
string k,m;
while(getline(file, k))
m += k ;
eraseTags(m,tags);
return 0;
}
Gives this message: "this application has requested the runtime to terminate it in an unusual way.Please contact the application's support team for more information."
If the string is not found, find returns string::npos, and then your code wouldn't work and will give runtime error. See this gives error : https://ideone.com/NEhqn
So better write this:
size_t pos = str.find(a[1]);
if ( pos != std::string::npos)
str.erase(pos); //str.size() is not needed!
Now this doesn't give error : https://ideone.com/IF2Hy
There's nothing wrong with that call (assuming a[1] exists and is found in str at least once)
#include <iostream>
#include <string>
int main()
{
std::string str = "Hello, world!";
std::string a = "wwwwww";
str.erase(str.find(a[1]), str.size());
std::cout << str << '\n';
}
test run: https://ideone.com/8wibR
EDIT: Your full source code fails to check if b[1] is actually found in str. The function boolStringExists() returns 1 if a.find(b) is greater than zero, and the value of std::string::npos which it returns when b is not found in a IS greater than zero.
To fix this while keeping the rest of your logic intact, change that function to
//check if string exists
bool boolStringExists(string a, string b)
{
return a.find(b) != string::npos;
}
It seems you want to erase everything that comes after str.find(a[1]). In that case you can omit the second argument.
#include <iostream>
#include <string>
int main(int argc, char *argv[]) {
std::string str = "Hello, world!";
std::string needle = "o,";
str.erase(str.find(needle));
std::cout << str << "\n";
}
In this example I used needle instead of a[1], but the principle is the same.