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.
Related
Disclaimer: it's been about 5 years since I wrote any C/C++. I usually code in python.
I have a vector of strings that I wanted to sort in a certain way (by length), so I wrote my own comparator function:
bool sortSubstrs(string a, string b)
{
if (a.length() > b.length()) { return true; }
else { return false; }
}
I had a bug in it initially, so I put in a print statement (yes, I know I should be using an ide, but I was being lazy and just using vim):
bool sortSubstrs(string a, string b)
{
cout << a.length() + " " + b.length() << endl;
if (a.length() > b.length()) { return true; }
else { return false; }
}
I expected it to print the length of a and b, but instead it prints a bunch of seemingly "random" stuff from earlier in the stack (I'm guessing).
Here's a full, minimal example to reproduce:
sort_words.cpp:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
vector<string> to_vector(char* filename) // read from file and put into a vector
{
vector<string> karray;
FILE *ifile = fopen(filename, "r");
int ch;
int idx = 0;
string s1 = "";
while( EOF != (ch=getc(ifile)))
{
if ('\n' == ch)
{
karray.push_back(s1);
s1 = "";
} else {
string s2(1,ch);
s1 = s1 + s2;
}
}
fclose(ifile);
return karray;
}
bool sortSubstrs(string a, string b)
{
cout << a.length() + " " + b.length() << endl;
if (a.length() > b.length()) { return true;}
else { return false;}
}
void sort_words(char* filename){
vector<string> wordArray;
wordArray = to_vector(filename);
sort(wordArray.begin(), wordArray.end(), sortSubstrs);
wordArray.erase(unique(wordArray.begin(), wordArray.end()), wordArray.end());
//print out the sorted array
for (unsigned n=0; n < wordArray.size(); n++) {cout << wordArray.at(n) << "\n";}
}
int main(int argc, char *argv[])
{
if (FILE *ifile = fopen(argv[1], "r")){
fclose(ifile);
sort_words(argv[1]);
} else {
cout << "file doesn't exist" << endl;
return 1;
}
}
and an input.txt file:
sh
zsh
bash
Compiling with g++ (4.8.5 running on centos7) gives no errors:
g++ sort_words.cpp -o sort_words
and running ./sort_words input.txt gives this output:
ile doesn't exist
e doesn't exist
bash
zsh
sh
I thought perhaps the check in main for the file existing/readable was somehow messing this up, so I rewrote it:
int main(int argc, char *argv[]) { sort_words(argv[1] }
but recompiling and running with the same input file gives this output instead:
ector::_M_insert_aux
tor::_M_insert_aux
bash
zsh
sh
Using a longer input file just prints out more of these "broken" strings in a similar pattern. So, what exactly is going on? My memory is a bit hazy, but I know that most sorting algorithms are recursive (and it looks like C++ uses a hybrid recursive sort method: Wikipedia: sort (C++)), but in any case the recursion occurs within the sort function and (I think) the sort function doesn't have access to the other functions in memory.
Does using a custom comparator significantly change how the sorting function works? My understanding was that inside the sort function, it would just call the custom function instead of the default > or < operators, but clearly something is a little different than I expected. Either that or I just have a bug somewhere that I've missed. I'm pretty stumped, unfortunately.
This has nothing to do with sort whatsoever. This line is wrong:
cout << a.length() + " " + b.length() << endl;
It should be:
cout << a.length() << " " << b.length() << endl;
As a relic and compatibility with C in C++ string literals have C array type, i.e. " " is of type char[2]. Again as a C relic C array types decay to pointers. So a.length() + " " is just pointer arithmetic. Not what you want.
Out of curiosity, what is happening when I use the +?
pointer arithmetics. You move the pointer from the beginning of the " " by
a.length() elements. Getting a pointer 1 past beyond the original array results in Undefined Behavior.
Here's a better example to illustrate:
const char str[7] = "abcdef";
str + 2
// is equivalent with:
&str[0] + 2
// and is a pointer pointing to the 'c' letter from the array
str + 10
// is equivalent with:
&str[0] + 10
// and is a pointer pointing outside the array
// this is Undefined Behavior
I am trying to do some file reading with C++ in Ubuntu 16.04 (GCC&G++ 5.4 and CMake 3.5.1).
The test file (named 123.txt) have only a line words just like this:
Reprojection error: avg = 0.110258 max = 0.491361
I just want to get the avg error and max error. My method is to get a line and put them into a std::string
and use string::find. My codes are very easy just like this:
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
FILE *fp = fopen("123.txt", "r");
char tmp[60];
string str;
fgets(tmp, size_t(tmp), fp);
fclose(fp);
cout << tmp << endl;
str = tmp;
cout << str.size() << endl;
size_t avg = str.find("avg");
size_t max = str.find("max");
cout << avg << endl;
cout << max << endl;
}
I can use g++ to compile it successfully. But I meet a strange issue.
When I first run it in the command, it will get the right result:
Reprojection error: avg = 0.110258 max = 0.491361
52
20
37
If I run codes again, it will go wrong sometimes just like this:
p
2
18446744073709551615
18446744073709551615
The "p" is a disorderly code which can not be shown correctly in the command. I am not good at C++ and feel confused about it. Is there someone who can say something? Thank you!
The expression
fgets(tmp, size_t(tmp), fp);
is ill-formed, size_t(tmp) will not work as you expect, you need sizeof(tmp).
The 52 value you get is because fgets consumes the \n character and this is counted too, actually the string has 51 characters counting with spaces.
That said, in this case you can use better C++ tools to replace the C ones you are using, fopen can be replaced by using the fstream library, fgets can be replaced by getline.
Something like:
#include <iostream>
#include <string>
#include <fstream>
int main()
{
std::ifstream fp("123.txt"); //C++ filestream
if (fp.is_open()) {//check for file opening errors
std::string str;
std::getline(fp, str); //C++ read from file
fp.close();
std::cout << str << std::endl;
std::cout << str.size() << std::endl;
size_t avg = str.find("avg");
size_t max = str.find("max");
std::cout << avg << std::endl;
std::cout << max << std::endl;
}
else{
std::cerr << "Couldn't open file";
}
}
Note that I dind't use using namespace std;, this is for a reason, it's not a good practice, you can check this thread for more details.
i want to be able to store data into a data.bin.gz using using zstr (a library that use zlib). I succeed to write into the file, but i cannot read it back. Here is a short example.
std::auto_ptr<std::ostream> ofs = std::auto_ptr<std::ostream>(new zstr::ofstream(fileName));
std::string str("hello world");
ofs.get()->write(str.c_str(), 11);
std::cout << "data sent: " << str << std::endl;
std::auto_ptr<std::istream> ifs = std::auto_ptr<std::istream>(new zstr::ifstream(fileName));
std::streamsize buffSize = 11;
char* buff = new char [11];
// fill buff to see if its content change
for (int i = 0; i < 11; i++) {
buff[i] = 'A';
}
ifs.get()->read(buff, buffSize);
std::cout << std::string(buff, buff+11) << std::endl;
delete [] buff;
i fill buff with some specfic content to see if it changes when reading the stream. but it does not change.
Here is a version that does approximately what you're asking for, but using standard file streams, not the non-standard zstr library which doesn't seem essential here:
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <vector>
using namespace std::string_literals;
int main()
{
constexpr auto fileName = "test.bin";
{
const auto str = "hello world"s;
auto ofs = std::ofstream( fileName, std::ios::binary );
ofs.write( str.data(), str.size() );
} // ofs is closed here by RAII
auto buff = std::vector<char>(100, 'A');
auto ifs = std::ifstream( fileName, std::ios::binary );
ifs.read(buff.data(), buff.size());
std::cout << std::string(buff.data(), buff.data()+11) << '\n';
}
It outputs hello world as expected. See it live on Coliru.
Notes:
I removed the auto_ptr and added the proper scoping.
I do not manage memory manually (new/delete), which is bad form. Instead I use std::vector and std::string.
I added the std::ios::binary flag to the fstream constructors to open in binary mode, since that is what it seems you ultimately want to do. This may not be needed with the zstr library you're using.
I made the buffer larger, as if I don't know what's in the file. Then I read from it as much space as I've allocated. When printing the result, I use the "insider knowledge" that there are 11 valid bytes. An alternative would be to initialize the vector to all zeros (the default) and just print it as a string:
auto buff = std::vector<char>( 100 );
auto ifs = std::ifstream( fileName, std::ios::binary );
ifs.read(buff.data(), buff.size() - 1); // Keep one zero for null terminator
std::cout << buff.data() << '\n';
which you can also see live on Coliru.
I also modernized in a few other ways just for fun and educational purposes:
I use constexpr on a constant known at compile-time.
I use the string literal suffix s on str to create a std::string with greater concision.
I use 'almost always auto' style for declaring objects.
Use \n instead of std::endl because you don't need the extra flush (good habit to be in).
Searching the net for examples how to pass command line parameters to a C++ code, I came up with an abandoned post where this process is being explained. This code was not working and after a few amendments I came up with the following (working) code:
#include <iostream>
#include <windows.h>
#include <fstream>
#include <string>
using namespace std;
// When passing char arrays as parameters they must be pointers
int main(int argc, char* argv[]) {
if (argc < 2) { // Check the value of argc. If not enough parameters have been passed, inform user and exit.
std::cout << "Usage is -i <index file name including path and drive letter>\n"; // Inform the user of how to use the program
std::cin.get();
exit(0);
} else { // if we got enough parameters...
char* indFile;
//std::cout << argv[0];
for (int i = 1; i < argc; i++) { /* We will iterate over argv[] to get the parameters stored inside.
* Note that we're starting on 1 because we don't need to know the
* path of the program, which is stored in argv[0] */
if (i + 1 != argc) {// Check that we haven't finished parsing already
if (strcmp(argv[i],"/x")==0) {
// We know the next argument *should* be the filename:
char indFile=*argv[i+1];
std::cout << "This is the value coming from std::cout << argv[i+1]: " << argv[i+1] <<"\n";
std::cout << "This is the value of indFile coming from char indFile=*argv[i+1]: " <<indFile <<"\n";
} else {
std::cout << argv[i];
std::cout << " Not enough or invalid arguments, please try again.\n";
Sleep(2000);
exit(0);
}
//std::cout << argv[i] << " ";
}
//... some more code
std::cin.get();
return 0;
}
}
}
Executing this code from the Windows command line using:
MyProgram.exe /x filename
returns the next output:
This is the attribute of parameter /x: filename
This is the value from *argv[i+1]: f
The original post from cplusplus.com did not compile; the code above does.
As you can see printing the argv[2] gives me the name of the file. When I try to capture the file name into another var so I can use it in the C++ program, I only get the first character (second response line).
Now for my question: How can I read the value from the command line parameter the pointer is pointing to?
Hope someone can help this newbie in C++ :-)
*argv[i+1]
Accesses the 1st char of the char* argv[] argument.
To get the whole value use something like
std::string filename(argv[i+1]);
instead.
You can't store a string in a single char.
Here's the once-an-idiom for copying the main arguments to more manageable objects:
#include <string>
#include <vector>
using namespace std;
void foo( vector<string> const& args )
{
// Whatever
(void) args;
}
auto main( int n, char* raw_args[] )
-> int
{
vector<string> const args{ raw_args, raw_args + n };
foo( args );
}
Do note that this code relies on an assumption that the encoding used for the main arguments can represent the actual command line arguments. That assumption holds in Unix-land, but not in Windows. In Windows, if you want to deal with non-ASCII text in command line arguments, you'd better use a third party solution or roll your own, e.g. using Windows' GetCommandLine API function.
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)