Get temp path with file name - c++

I want to get path to file like this > %ENV%/%FILE_NAME%.docx
But c++ doesn't make sense at all and nothing works..
I would use std::string but it's not compatible so I tried multiple ways of converting it to char[] or char* but none of them works and I'm also pretty sure this is unsafe..
My code so far (I know it's the worst code ever..)
char* appendCharToCharArray(char* array, char a)
{
size_t len = strlen(array);
char* ret = new char[len + 2];
strcpy(ret, array);
ret[len] = a;
ret[len + 1] = '\0';
return ret;
}
const char* getBaseName(std::string path)
{
std::string base_filename = path.substr(path.find_last_of("/\\") + 1);
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
return file_without_extension.c_str();
}
int main()
{
char szExeFileName[MAX_PATH];
GetModuleFileName(NULL, szExeFileName, MAX_PATH);
const char* file_name = getBaseName(std::string(szExeFileName));
char* new_file = getenv("temp");
new_file = appendCharToCharArray(new_file, '\\');
for (int i=0;i<sizeof(file_name)/sizeof(file_name[0]);i++)
{
new_file = appendCharToCharArray(new_file, file_name[i]);
}
new_file = appendCharToCharArray(new_file, '.');
new_file = appendCharToCharArray(new_file, 'd');
new_file = appendCharToCharArray(new_file, 'o');
new_file = appendCharToCharArray(new_file, 'c');
new_file = appendCharToCharArray(new_file, 'x');
std::cout << new_file << std::endl;
}

Using appendCharToCharArray() is just horribly inefficient in general, and also you are leaking lots of memory with the way you are using it. Just use std::string instead. And yes, you can use std::string in this code, it is perfectly "compatible" if you use it correctly.
getBaseName() is returning a char* pointer to the data of a local std::string variable that goes out of scope when the function exits, thus a dangling pointer is returned. Again, use std::string instead.
And, you should use the Win32 GetTempPath/2() function instead of getenv("temp").
Try something more like this:
#include <iostream>
#include <string>
std::string getBaseName(const std::string &path)
{
std::string base_filename = path.substr(path.find_last_of("/\\") + 1);
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
return file_without_extension;
}
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
std::string new_file = std::string(szTempFolder) + getBaseName(szExeFileName) + ".docx";
std::cout << new_file << std::endl;
}
Online Demo
That being said, the Win32 Shell API has functions for manipulating path strings, eg:
#include <iostream>
#include <string>
#include <windows.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
char new_file[MAX_PATH] = {};
PathCombineA(new_file, szTempFolder, PathFindFileNameA(szExeFileName));
PathRenameExtensionA(new_file, ".docx");
std::cout << new_file << std::endl;
}
Or, if you are using C++17 or later, consider using std::filesystem::path instead, eg:
#include <iostream>
#include <filesystem>
#include <windows.h>
namespace fs = std::filesystem;
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
fs::path new_file = fs::path(szTempFolder) / fs::path(szExeFileName).stem();
new_file += ".docx";
// alternatively:
// fs::path new_file = fs::path(szTempFolder) / fs::path(szExeFileName).filename();
// new_file.replace_extension(".docx");
std::cout << new_file << std::endl;
}
Online Demo

Related

How do I use the L switch with a variable name?

I have this code:
#include <iostream>
#include <io.h>
#include <fcntl.h>
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
std::string t_prefix = "\u2554";
std::string t_middle = "";
std::string t_suffix = "\u2557\n\u2551";
int max_message_length = 33;
for (int i = t_middle.length(); i < max_message_length; i++) {
t_middle = "\u2550" + t_middle;
}
std::string complete_message = t_prefix + t_middle + t_suffix;
std::wcout << L"\u2554" << t_middle.c_str() << L"\u2557\n\u2551";
std::wcout << complete_message.c_str();
//std::wcout << L"\u2554\n";
}
The first wout line prints the first and last part right, the middle section prints out garbage because it does not have the L in it. (How do I use the L with a variable?)
The second wout line combines the prefix, message, and suffix prints out garbage since it needs the L switch to print properly? I find a million of examples of how to do it with straight hard coded text L"\u2554\u2550\u2550" ect ect but I wanted the program to be a little more flexible so the game boarder could be wider if so set.
This seems to work and do what I need it to do at least for windows:
#include <io.h>
#include <fcntl.h>
#include <iostream>
#include <string>
int main()
{
_setmode(_fileno(stdout), _O_U8TEXT);
std::wstring t_prefix { L'\u2554' };
std::wstring t_suffix { L"\u2557\n\u2551" };
std::wstring t_middle;
const size_t max_message_length { 33 };
for (size_t i { t_middle.size() }; i < max_message_length; ++i)
{
t_middle += L'\u2550';
}
std::wstring complete_message { t_prefix + t_middle + t_suffix };
std::wcout << complete_message << '\n';
}
and this is suppose to work on linux:
#include <iostream>
int main()
{
std::string t_prefix = "╔";
std::string t_middle;
std::string t_suffix = "╗\n║";
int max_message_length = 33;
for (int i = t_middle.size(); i < max_message_length; ++i)
t_middle = "═" + t_middle;
std::string complete_message = t_prefix + t_middle + t_suffix;
std::cout << complete_message << '\n';
}

How to convert from UTF-8 to ANSI using standard c++

I have some strings read from the database, stored in a char* and in UTF-8 format (you know, "á" is encoded as 0xC3 0xA1). But, in order to write them to a file, I first need to convert them to ANSI (can't make the file in UTF-8 format... it's only read as ANSI), so that my "á" doesn't become "á". Yes, I know some data will be lost (chinese characters, and in general anything not in the ANSI code page) but that's exactly what I need.
But the thing is, I need the code to compile in various platforms, so it has to be standard C++ (i.e. no Winapi, only stdlib, stl, crt or any custom library with available source).
Anyone has any suggestions?
A few days ago, somebody answered that if I had a C++11 compiler, I could try this:
#include <string>
#include <codecvt>
#include <locale>
string utf8_to_string(const char *utf8str, const locale& loc)
{
// UTF-8 to wstring
wstring_convert<codecvt_utf8<wchar_t>> wconv;
wstring wstr = wconv.from_bytes(utf8str);
// wstring to string
vector<char> buf(wstr.size());
use_facet<ctype<wchar_t>>(loc).narrow(wstr.data(), wstr.data() + wstr.size(), '?', buf.data());
return string(buf.data(), buf.size());
}
int main(int argc, char* argv[])
{
string ansi;
char utf8txt[] = {0xc3, 0xa1, 0};
// I guess you want to use Windows-1252 encoding...
ansi = utf8_to_string(utf8txt, locale(".1252"));
// Now do something with the string
return 0;
}
Don't know what happened to the response, apparently someone deleted it. But, turns out that it is the perfect solution. To whoever posted, thanks a lot, and you deserve the AC and upvote!!
If you mean ASCII, just discard any byte that has bit 7 set, this will remove all multibyte sequences. Note that you could create more advanced algorithms, like removing the accent from the "á", but that would require much more work.
This should work:
#include <string>
#include <codecvt>
using namespace std::string_literals;
std::string to_utf8(const std::string& str, const std::locale& loc = std::locale{}) {
using wcvt = std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>;
std::u32string wstr(str.size(), U'\0');
std::use_facet<std::ctype<char32_t>>(loc).widen(str.data(), str.data() + str.size(), &wstr[0]);
return wcvt{}.to_bytes(wstr.data(),wstr.data() + wstr.size());
}
std::string from_utf8(const std::string& str, const std::locale& loc = std::locale{}) {
using wcvt = std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>;
auto wstr = wcvt{}.from_bytes(str);
std::string result(wstr.size(), '0');
std::use_facet<std::ctype<char32_t>>(loc).narrow(wstr.data(), wstr.data() + wstr.size(), '?', &result[0]);
return result;
}
int main() {
auto s0 = u8"Blöde C++ Scheiße äöü!!1Elf"s;
auto s1 = from_utf8(s0);
auto s2 = to_utf8(s1);
return 0;
}
For VC++:
#include <string>
#include <codecvt>
using namespace std::string_literals;
std::string to_utf8(const std::string& str, const std::locale& loc = std::locale{}) {
using wcvt = std::wstring_convert<std::codecvt_utf8<int32_t>, int32_t>;
std::u32string wstr(str.size(), U'\0');
std::use_facet<std::ctype<char32_t>>(loc).widen(str.data(), str.data() + str.size(), &wstr[0]);
return wcvt{}.to_bytes(
reinterpret_cast<const int32_t*>(wstr.data()),
reinterpret_cast<const int32_t*>(wstr.data() + wstr.size())
);
}
std::string from_utf8(const std::string& str, const std::locale& loc = std::locale{}) {
using wcvt = std::wstring_convert<std::codecvt_utf8<int32_t>, int32_t>;
auto wstr = wcvt{}.from_bytes(str);
std::string result(wstr.size(), '0');
std::use_facet<std::ctype<char32_t>>(loc).narrow(
reinterpret_cast<const char32_t*>(wstr.data()),
reinterpret_cast<const char32_t*>(wstr.data() + wstr.size()),
'?', &result[0]);
return result;
}
int main() {
auto s0 = u8"Blöde C++ Scheiße äöü!!1Elf"s;
auto s1 = from_utf8(s0);
auto s2 = to_utf8(s1);
return 0;
}
#include <stdio.h>
#include <string>
#include <codecvt>
#include <locale>
#include <vector>
using namespace std;
std::string utf8_to_string(const char *utf8str, const locale& loc){
// UTF-8 to wstring
wstring_convert<codecvt_utf8<wchar_t>> wconv;
wstring wstr = wconv.from_bytes(utf8str);
// wstring to string
vector<char> buf(wstr.size());
use_facet<ctype<wchar_t>>(loc).narrow(wstr.data(), wstr.data() + wstr.size(), '?', buf.data());
return string(buf.data(), buf.size());
}
int main(int argc, char* argv[]){
std::string ansi;
char utf8txt[] = {0xc3, 0xa1, 0};
// I guess you want to use Windows-1252 encoding...
ansi = utf8_to_string(utf8txt, locale(".1252"));
// Now do something with the string
return 0;
}

std::string -> execvp etc

I just found an elusive bug in a program and it turned out to be because with optimization enabled, in something like the following sometimes the std::string is destroyed before processDocument() got the text out of it:
#include <stdio.h>
#include <spawn.h>
#include <string>
static void processDocument(const char* text) {
const char* const argv[] = {
"echo",
text,
NULL,
};
pid_t p;
posix_spawnp(&p, "echo", NULL, NULL, (char**) argv, environ);
}
static int mark = 'A';
static void createDocument() {
const char* vc;
std::string v = "ABCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK42";
++mark;
v[0] = mark;
vc = v.c_str();
processDocument(vc);
}
int main() {
createDocument();
createDocument();
return(0);
}
How do I safely convert a std::string to a char* for use in execvp, posix_spawnp etc ?
I found out why it really was (here the actual minimal testcase):
std::string resultString;
const char* nodeText;
const char* altText;
resultString = "......whatever1.";
nodeText = resultString.c_str();
resultString = ".....whatever2..";
altText = resultString.c_str();
printf("%s\n", nodeText); // garbage
Bad idea.

How to tell where the application is being ran in C++

I'm using wget along side my app to download a jar and run it. Problem is with my current setup wget.exe would have to be kept in a folder in the app data and that really isn't smart i.e. how would the file get there to begin with?
So how would one find the directory the app is being run in no matter where it is being ran from?
for windows:
std::string calculateRunPath()
{
const unsigned int size = 500;
char buf[size] = {0};
HMODULE hModule = GetModuleHandle(NULL);
GetModuleFileName(hModule,buf, sizeof(buf));
std::string path(buf);
size_t pos = path.find_last_of('\\');
return path.substr(0, pos);
}
for Linux:
std::string calculateRunPath()
{
const unsigned int size = 500;
char path[size + 1] = {0};
size_t len = readlink("/proc/self/exe", path, size);
path[len] = 0;
char* p = strrchr(path, '/');
if(p)
*(p + 1) = 0;
else
path[0] = 0;
return std::string(path);
}
Some boost filesystem goodness should work too, something like...
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <iostream>
int main()
{
std::cout << boost::filesystem::current_path().string() << std::endl;
return 0;
}
You have to read the PWD environment variable

how to convert char array to wchar_t array?

char cmd[40];
driver = FuncGetDrive(driver);
sprintf_s(cmd, "%c:\\test.exe", driver);
I cannot use cmd in
sei.lpFile = cmad;
so,
how to convert char array to wchar_t array ?
Just use this:
static wchar_t* charToWChar(const char* text)
{
const size_t size = strlen(text) + 1;
wchar_t* wText = new wchar_t[size];
mbstowcs(wText, text, size);
return wText;
}
Don't forget to call delete [] wCharPtr on the return result when you're done, otherwise this is a memory leak waiting to happen if you keep calling this without clean-up. Or use a smart pointer like the below commenter suggests.
Or use standard strings, like as follows:
#include <cstdlib>
#include <cstring>
#include <string>
static std::wstring charToWString(const char* text)
{
const size_t size = std::strlen(text);
std::wstring wstr;
if (size > 0) {
wstr.resize(size);
std::mbstowcs(&wstr[0], text, size);
}
return wstr;
}
From MSDN:
#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;
using namespace System;
int main()
{
char *orig = "Hello, World!";
cout << orig << " (char *)" << endl;
// Convert to a wchar_t*
size_t origsize = strlen(orig) + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t wcstring[newsize];
mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);
wcscat_s(wcstring, L" (wchar_t *)");
wcout << wcstring << endl;
}
From your example using swprintf_s would work
wchar_t wcmd[40];
driver = FuncGetDrive(driver);
swprintf_s(wcmd, "%C:\\test.exe", driver);
Note the C in %C has to be written with uppercase since driver is a normal char and not a wchar_t.
Passing your string to swprintf_s(wcmd,"%S",cmd) should also work