I have a filename (C:\folder\foo.txt) and I need to retrieve the folder name (C:\folder) in C++. In C# I would do something like this:
string folder = new FileInfo("C:\folder\foo.txt").DirectoryName;
Is there a function that can be used in C++ to extract the path from the filename?
Using Boost.Filesystem:
boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
Example from http://www.cplusplus.com/reference/string/string/find_last_of/
// string::find_last_of
#include <iostream>
#include <string>
using namespace std;
void SplitFilename (const string& str)
{
size_t found;
cout << "Splitting: " << str << endl;
found=str.find_last_of("/\\");
cout << " folder: " << str.substr(0,found) << endl;
cout << " file: " << str.substr(found+1) << endl;
}
int main ()
{
string str1 ("/usr/bin/man");
string str2 ("c:\\windows\\winhelp.exe");
SplitFilename (str1);
SplitFilename (str2);
return 0;
}
In C++17 there exists a class std::filesystem::path using the method parent_path.
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
for(fs::path p : {"/var/tmp/example.txt", "/", "/var/tmp/."})
std::cout << "The parent path of " << p
<< " is " << p.parent_path() << '\n';
}
Possible output:
The parent path of "/var/tmp/example.txt" is "/var/tmp"
The parent path of "/" is ""
The parent path of "/var/tmp/." is "/var/tmp"
There is a standard Windows function for this, PathRemoveFileSpec. If you only support Windows 8 and later, it is highly recommended to use PathCchRemoveFileSpec instead. Among other improvements, it is no longer limited to MAX_PATH (260) characters.
Why does it have to be so complicated?
#include <windows.h>
int main(int argc, char** argv) // argv[0] = C:\dev\test.exe
{
char *p = strrchr(argv[0], '\\');
if(p) p[0] = 0;
printf(argv[0]); // argv[0] = C:\dev
}
auto p = boost::filesystem::path("test/folder/file.txt");
std::cout << p.parent_path() << '\n'; // test/folder
std::cout << p.parent_path().filename() << '\n'; // folder
std::cout << p.filename() << '\n'; // file.txt
You may need p.parent_path().filename() to get name of parent folder.
Use boost::filesystem. It will be incorporated into the next standard anyway so you may as well get used to it.
I'm so surprised no one has mentioned the standard way in Posix
Please use basename / dirname constructs.
man basename
_splitpath is a nice CRT solution.
Standard C++ won't do much for you in this regard, since path names are platform-specific. You can manually parse the string (as in glowcoder's answer), use operating system facilities (e.g. http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ), or probably the best approach, you can use a third-party filesystem library like boost::filesystem.
Just use this: ExtractFilePath(your_path_file_name)
Related
Today I did a lot of research online about how to create a directory on C++
and found a lot of way to do that, some easier than others.
I tried the _mkdir function using _mkdir("C:/Users/..."); to create a folder. Note that the argument of function will be converted into a const char*.
So far, so good, but when I want to change the path, it does not work (see the code below). I have a default string path "E:/test/new", and I want to create 10 sub-folders: new1, new2, newN, ..., new10.
To do that, I concatenate the string with a number (the counter of the for-loop), converted into char using static_cast, then I transform the string using c_str(), and assign it to a const char* variable.
The compiler has no problem compiling it, but it doesn't work. It prints 10 times "Impossible create folder n". What's wrong?
I probably made a mistake when transforming the string using c_str() to a get a const char*?.
Also, is there a way to create a folder using something else? I looked at CreateDirectory(); (API) but it uses keyword like DWORD HANDLE, etc., that are a little bit difficult to understand for a no-advanced level (I don't know what these mean).
#include <iostream>
#include <Windows.h>
#include<direct.h>
using namespace std;
int main()
{
int stat;
string path_s = "E:/test/new";
for (int i = 1; i <= 10; i++)
{
const char* path_c = (path_s + static_cast<char>(i + '0')).c_str();
stat = _mkdir(path_c);
if (!stat)
cout << "Folder created " << i << endl;
else
cout << "Impossible create folder " << i << endl;
Sleep(10);
}
return 0;
}
If your compiler supports c++17, you can use filesystem library to do what you want.
#include <filesystem>
#include <string>
#include <iostream>
namespace fs = std::filesystem;
int main(){
const std::string path = "E:/test/new";
for(int i = 1; i <= 10; ++i){
try{
if(fs::create_directory(path + std::to_string(i)))
std::cout << "Created a directory\n";
else
std::cerr << "Failed to create a directory\n";\
}catch(const std::exception& e){
std::cerr << e.what() << '\n';
}
}
return 0;
}
The problem is that (path_s + static_cast<char>(i + '0')) creates a temporary object. One whose life-time ends (and is destructed) just after c_str() has been called.
That leaves you with a pointer to a string that no longer exist, and using it in almost any way will lead to undefined behavior.
Instead save the std::string object, and call c_str() just when needed:
std::string path = path_s + std::to_string(i);
_mkdir(path.c_str());
Note that under Linux, you can use the mkdir command as follows:
#include <sys/stat.h>
...
const int dir_err = mkdir("foo", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (-1 == dir_err){
printf("Error creating directory!n");
exit(1);
}
More information on it can be gleaned from reading man 2 mkdir.
I'm trying to expand my C++ game hacking skills as when I was starting (2 years ago) I made a bad decision: continue in game hacking with vb.net instead of learning c++ (as I had some vb.net knowledge and 0 knowledge with other languages)
So, now as the very first steps I have to create my toolkit, where I will be using my own templates:
Nathalib.h (my template with all common functions for game hacking).
#pragma once
#include <iostream>
#include <Windows.h>
#include <string>
#include <TlHelp32.h>
#include <stdio.h>
using namespace std;
DWORD ProcessID;
int FindProcessByName(string name)
{
HWND hwnd = FindWindowA(0, name);
GetWindowThreadProcessId(hwnd, &ProcessID);
if (hwnd)
{
return ProcessID;
}
else
{
return 0;
}
}
Hack.cpp (obviously the cheat, will be different for every game).
#pragma once
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <string>
#include <Nathalib.h>
using namespace std;
int main()
{
While(True)
{
cout << FindProcessByName("Calculator") << endl;
getchar();
cout << "-----------------------------------" << endl << endl;
}
return 0;
}
Target.cpp (as we're not bad boys, I must provide my own target).
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;
#define CHAR_ARRAY_SIZE 128
int main()
{
int varInt = 123456;
string varString = "DefaultString";
char arrChar[CHAR_ARRAY_SIZE] = "Long char array right there ->";
int * ptr2int;
ptr2int = &varInt;
int ** ptr2ptr;
ptr2ptr = &ptr2int;
int *** ptr2ptr2;
ptr2ptr2 = &ptr2ptr;
while(True) {
cout << "Process ID: " << GetCurrentProcessId() << endl;
cout << "varInt (0x" << &varInt << ") = " << varInt << endl;
cout << "varString (0x" << &varString << ") = " << varString << endl;
cout << "varChar (0x" << &arrChar << ") = " << arrChar << endl;
cout << "ptr2int (0x" << hex << &ptr2int << ") = " << ptr2int << endl;
cout << "ptr2ptr (0x" << hex << &ptr2ptr << ") = " << ptr2ptr << endl;
cout << "ptr2ptr2 (0x" << hex << &ptr2ptr2 << ") = " << ptr2ptr2 << endl;
cout << "Press ENTER to print again." << endl;
getchar();
cout << "-----------------------------------" << endl << endl;
}
return 0;
}
I don't know why the header file is not being recognized.
This is the correct way to include header files? Should I create a namespace/class/object for calling it?
It's the correct way creating a header file? Or I should create another kind of project/resource for this purpose?
How should I call my library methods? Like LibraryName.MethodName?
I just come from other languages and some ideas/features are not available in the other languages (that's why I'm interested in this one)
If there's something I forgot to add, please tell me and I will update
Thanks
There are multiple errors - please check your textbook.
You include your own headers with #include "". System headers are included with #include<>
The header file generally contains function declarations. Function bodies go into the corresponding .cpp file.
You call your library functions by their name. If they're in a namespace, that might mean the format is namespacename::functionname(arguments).
There are two ways to include headers, using "" or <>
with <> the file will be searched in the system search path (which is not the $PATH variabel, but the list of paths provided with `-I' together with standard headers already known by compiler) and included if found
with "" the file will be search in the current folder and in the system search path
Assuming your header is in th esame folder of hack.cpp, you should use
#include "Nathalib.h"
First off, your header lacks include guards, #pragma once only works with msvc++.
Your header file is probably not in PATH, so you need to specify it's path relative to your project. If your header file is in the same root as your cpp file, all you need to do is change the include statement for that header file to #include "Nathalib.h" otherwise you'll have to specify the relative path.
To add to other aswers- why you should put declaration of function in .h file, while its definition to .cpp file: Writing function definition in header files in C++
I suggest to find some c++ tutorials for example: http://www.tutorialspoint.com/cplusplus/cpp_functions.htm
You should learn tutorials first, making some exercises on simply code. Personally I prefer check then most simply code for new programming construct. Then more complicated.
After such learning you may use for reference also : http://www.cplusplus.com and https://en.cppreference.com/w/
I'm a C++ programmer, who's still in the nest, and not yet found my wings. I was writing a Calendar program, and I discovered, that C++ does not support a string type. How do I make an Array, that will be able to store strings of characters?
I've thought of creating an enumerated data type, as the array type. While, it will work, for my Calendar, it won't work if say I was creating a database of the names of students in my class.
http://prntscr.com/7m074w I got; "error, 'string' does not name a type."
that C++ does not support a string type.
Wrong info, you can create an character array as follows
char array[length];
//Where length should be a constant integer
Otherwise you can depend on standard template library container, std::string
If you have C++11 compiler you can depend on std::array
The C++ Standard Library includes a string type, std::string. See http://en.cppreference.com/w/cpp/string/basic_string
The Standard Library also provides a fixed-size array type, std::array. See http://en.cppreference.com/w/cpp/container/array
But you may also want to learn about the dynamically-sized array type, std::vector. See http://en.cppreference.com/w/cpp/container/vector
The language also includes legacy support for c-strings and c-arrays, which you can find in a good C++ or C book. See The Definitive C++ Book Guide and List
An example of how to use an array/vector of strings:
#include <string>
#include <array>
#include <vector>
#include <iostream>
int main() {
std::array<std::string, 3> stringarray;
stringarray[0] = "hello";
stringarray[1] = "world";
// stringarray[2] contains an empty string.
for (size_t i = 0; i < stringarray.size(); ++i) {
std::cout << "stringarray[" << i << "] = " << stringarray[i] << "\n";
}
// Using a vector, which has a variable size.
std::vector<std::string> stringvec;
stringvec.push_back("world");
stringvec.insert(stringvec.begin(), "hello");
stringvec.push_back("greetings");
stringvec.push_back("little bird");
std::cout << "size " << stringvec.size()
<< "capacity " << stringvec.capacity()
<< "empty? " << (stringvec.empty() ? "yes" : "no")
<< "\n";
// remove the last element
stringvec.pop_back();
std::cout << "size " << stringvec.size()
<< "capacity " << stringvec.capacity()
<< "empty? " << (stringvec.empty() ? "yes" : "no")
<< "\n";
std::cout << "stringvec: ";
for (auto& str : stringvec) {
std::cout << "'" << str << "' ";
}
std::cout << "\n";
// iterators and string concatenation
std::string greeting = "";
for (auto it = stringvec.begin(); it != stringvec.end(); ++it) {
if (!greeting.empty()) // add a space between words
greeting += ' ';
greeting += *it;
}
std::cout << "stringvec combined :- " << greeting << "\n";
}
Live demo: http://ideone.com/LWYevW
You can create an array of characters by char name[length];.
C++ also has a data type string. You can create an array of strings and store what values you'd like. here .
So
use array of characters
use string data type
For Example -
#include <iostream>
#include <string>
int main ()
{
//To Create a String
std::string s0 ("Initial string");
return 0;
}
C++ does have a string type: string from #include <string>
If you don't want to use that, you can also use char* name = "YourTextHere..." or `char[length+1] name = "YourTextHere"
I am working on a project to make a database of the files I have on current directory. And one of the details I want about my files is the file permissions that are set with chmod in ubuntu. (just a note: I will be needing the group and owner info too - like chown- and if you could let me know if boost can retrieve the ownership info too that'd be great.)
I am using boost filesystem library and I have checked the documentation for numerous times but couldn't find how to get the permissions.
In this page it shows that there's enum perms that has the file permission strings which doesn't show up on my own filesystem.hpp. (And I have checked that i've got the 1.49 version, also built from the source just to be sure). Also on the same page here it shows that it can get the permissions like:
perms permissions() const noexcept;
//Returns: The value of
//permissions() specified by the postconditions of the most recent call
//to a constructor, operator=, or permissions(perms) function.
I haven't been able to find the permissions function nor the place where it stores the perms list.
This is the code I have so far (which is actually from boost tutorials, but I modified it to be recursive), if you could tell me how to get the file permissions/ownerships or suggest another library than boost I would appreciate it.
EDIT: I have added the s.permissions() as ethan_liou suggested however the output was not as expected. Here's the updated code and the output.
// filesystem tut4.cpp ---------------------------------------------------------------//
// Copyright Beman Dawes 2009
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
// Library home page: http://www.boost.org/libs/filesystem
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <stdio.h>
using namespace std;
using namespace boost::filesystem;
int read(path p);
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut4 path\n";
return 1;
}
path p (argv[1]); // p reads clearer than argv[1] in the following code
read(p);
return 0;
}
int read(path p) {
try
{
if (exists(p)) // does p actually exist?
{
if (is_symlink(p)) {
cout << p << " is a link\n";
}
else if (is_regular_file(p)) {
// is p a regular file?
file_status s = status(p);
cout << p << " size is " << file_size(p) << " perms " << "" ;
printf("%o\n",s.permissions());
}
else if (is_directory(p)) // is p a directory?
{
cout << p << " is a directory containing:\n";
typedef vector<path> vec; // store paths,
vec v; // so we can sort them later
copy(directory_iterator(p), directory_iterator(), back_inserter(v));
sort(v.begin(), v.end()); // sort, since directory iteration
// is not ordered on some file systems
for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
{
//cout << " " << *it << '\n';
read(*it);
}
}
else
cout << p << " exists, but is neither a regular file nor a directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
Output:
$ ./a.out ~/Desktop/test
"/home/usr/Desktop/test" is a directory containing:
"/home/usr/Desktop/test/a.out" size is 69446 perms 27746424350
"/home/usr/Desktop/test/la" is a directory containing:
"/home/usr/Desktop/test/la/Untitled Document" size is 0 perms 27746424170
"/home/usr/Desktop/test/la/lala" is a directory containing:
"/home/usr/Desktop/test/la/lala/Link to lalalala" is a link
"/home/usr/Desktop/test/la/lala/Untitled Folder" is a directory containing:
"/home/usr/Desktop/test/la/lala/lalalala" size is 0 perms 0
"/home/usr/Desktop/test/test.cpp" size is 2234 perms 0
"/home/usr/Desktop/test/test.cpp~" size is 2234 perms 0
Note: Those numbers that are like 27746424350 change each time the program is executed.
perms permissions() const { return m_perms; }
defined in boost/filesystem/v3/operations.hpp
Add an easy sample code
#include <boost/filesystem.hpp>
#include <stdio.h>
namespace fs=boost::filesystem;
int main(int argc,char * argv[]){
fs::path p(argv[1]);
fs::file_status s = status(p);
printf("%o\n",s.permissions());
}
File permissions example for windows:
unsigned long attributes = ::GetFileAttributes( filePath.file_string().c_str());
if ( attributes != 0xFFFFFFFF && ( attributes & FILE_ATTRIBUTE_READONLY ))
{
attributes &= ~FILE_ATTRIBUTE_READONLY;
::SetFileAttributes( filePath.file_string().c_str(), attributes );
}
So... I have a base path and a new path.New path contains in it base path. I need to see what is different in new path. Like we had /home/ and new path is /home/apple/one and I need to get from it apple/one. note - when I would create some path from (homePath/diffPath) I need to get that /home/apple/one again. How to do such thing with Boost FileSystem?
Using stem() and parent_path() and walk backwards from the new path until we get back to base path, this works, but I am not sure if it is very safe.
Be cautious, as the path "/home" and "/home/" are treated as different paths. The below only works if base path is /home (without trailing slash) and new path is guaranteed to be below base path in the directory tree.
#include <iostream>
#include <boost/filesystem.hpp>
int main(void)
{
namespace fs = boost::filesystem;
fs::path basepath("/home");
fs::path newpath("/home/apple/one");
fs::path diffpath;
fs::path tmppath = newpath;
while(tmppath != basepath) {
diffpath = tmppath.stem() / diffpath;
tmppath = tmppath.parent_path();
}
std::cout << "basepath: " << basepath << std::endl;
std::cout << "newpath: " << newpath << std::endl;
std::cout << "diffpath: " << diffpath << std::endl;
std::cout << "basepath/diffpath: " << basepath/diffpath << std::endl;
return 0;
}
Assuming you have:
namespace fs = std::filesystem; // or boost::filesystem
fs::path base = "/home/usera"
fs::path full = "/home/usera/documents/doc"
If you want to extract documents/doc, you can do that with lexically_relative:
fs::path diff = full.lexically_relative(base);
assert( diff == fs::path("documents/doc") );
This works for base = "/home/usera" or base = "home/usera/". If full does not contain base, this may give you a pretty long path with lots of .. instead of getting an error.
std::filesystem::path::lexically_relative requires C++17
Other solution, if you know that newpath really belongs to basepath, could be:
auto nit = newpath.begin();
for (auto bit = basepath.begin(); bit != basepath.end(); ++bit, ++nit)
;
fs::path = path(nit, newpath.end());