Working for the first time in the area of unicode and widechars,
I am trying to convert WCHAR* to char*.
(WCHAR typedefed to SQLWCHAR, and eventually to unsigned short)
And I need to support all platforms(windows, mac, linux).
What I have as input is a WCHAR* and its length.
I figured, if I convert the input to wstring, I will have a chance to then strcpy/ strdup it to the output variable.
But looks like I am not constructing my wstring correctly because wprintf doesn't print its value.
Any hints what I am missing?
#include <iostream>
#include <codecvt>
#include <locale>
SQLRETURN SQL_API SQLExecDirectW(SQLHSTMT phstmt, SQLWCHAR* pwCmd, SQLINTEGER len)
{
char *output;
wchar_to_utf8(pwCmd, len, output);
// further processing
SQLRETURN rc;
return rc;
}
int wchar_to_utf8(WCHAR *wStr, int size, char *output)
{
std::wstring ws((const wchar_t*)wStr, size - 1);
wprintf(L"wstring:%s\n", ws);
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
std::string t = conv.to_bytes(ws);
/*allocate output or use strdup*/
strncpy(output, t.c_str(), size); // todo:take care of the last null char
return strlen(output);
}
"%s" in a wprintf format string requires a wchar_t* argument, so
wprintf(L"wstring%s\n", ws);
should be
wprintf(L"wstring%s\n", ws.c_str());
To convert wchar* to char*
const std::wstring wStr = /*your WCHAR* */;
const char* str = std::filesystem::path(wStr).string().c_str();
Related
I want concatenate a random string with a directory name and the final result must be something like this:
C:\Program Files (x86)\AAAFFF1334
On following code this part: "AAAFFF1334" comes strange characters see:
What must be made to fix this?
TCHAR mydir[MAX_PATH];
void gen_random(char *s, const int len) {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < len; ++i) {
s[i] = alphanum[rand() % (sizeof(alphanum)-1)];
}
s[len] = 0;
}
// main
TCHAR szProgramFiles[MAX_PATH];
HRESULT hProgramFiles = SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szProgramFiles);
char str;
gen_random(&str, 10);
wsprintf(mydir, TEXT("%s\\%s"), szProgramFiles, str);
gen_random should get char array with at least 11 characters (10 for size + 1 for terminating null).
So it should be:
char str[10+1]; //or char str[11];
gen_random(str, 10);
in addition, the format string should be: "%s\\%hs", the first is TCHAR* type (if UNICODE defined wchar_t* if not char*) the second is always char* type.
hs, hS
String. This value is always interpreted as type LPSTR, even
when the calling application defines Unicode.
look here
Note: in Microsoft documentation:
LPSTR = always char*
LPWSTR = always wchar_t*
LPTSTR = TCHAR* (if UNICODE defined: wchar_t*, else: char*)
Basically I need to convert UTF-8 string to windows-1256 and I do it using following code:
#include <windows.h>
#include <stdio.h>
char* convert(char* pszStringToConvert)
{
int len = strlen(pszStringToConvert); // get string length
wchar_t* pwsz = new wchar_t[len+1]; // allocate storage for temporary UNICODE string
MultiByteToWideChar(65001,0, pszStringToConvert, len, pwsz, len);
WideCharToMultiByte(1256,0, pwsz, len, pszStringToConvert, len, NULL, FALSE);
return pszStringToConvert;
}
int main()
{
char* arabic ="السلام";
char* win = convert(arabic);
printf("%s\n%",win);
return 0;
}
My source string is
arabic ="السلام"
But unfortunately my result string becomes
win = السلام
What I'm doing wrong here?
I'm new to C++ and I have this issue. I have a string called DATA_DIR that I need for format into a wstring.
string str = DATA_DIR;
std::wstring temp(L"%s",str);
Visual Studio tells me that there is no instance of constructor that matches with the argument list. Clearly, I'm doing something wrong.
I found this example online
std::wstring someText( L"hello world!" );
which apparently works (no compile errors). My question is, how do I get the string value stored in DATA_DIR into the wstring constructor as opposed to something arbitrary like "hello world"?
Here is an implementation using wcstombs (Updated):
#include <iostream>
#include <cstdlib>
#include <string>
std::string wstring_from_bytes(std::wstring const& wstr)
{
std::size_t size = sizeof(wstr.c_str());
char *str = new char[size];
std::string temp;
std::wcstombs(str, wstr.c_str(), size);
temp = str;
delete[] str;
return temp;
}
int main()
{
std::wstring wstr = L"abcd";
std::string str = wstring_from_bytes(wstr);
}
Here is a demo.
This is in reference to the most up-voted answer but I don't have enough "reputation" to just comment directly on the answer.
The name of the function in the solution "wstring_from_bytes" implies it is doing what the original poster wants, which is to get a wstring given a string, but the function is actually doing the opposite of what the original poster asked for and would more accurately be named "bytes_from_wstring".
To convert from string to wstring, the wstring_from_bytes function should use mbstowcs not wcstombs
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdlib>
#include <string>
std::wstring wstring_from_bytes(std::string const& str)
{
size_t requiredSize = 0;
std::wstring answer;
wchar_t *pWTempString = NULL;
/*
* Call the conversion function without the output buffer to get the required size
* - Add one to leave room for the NULL terminator
*/
requiredSize = mbstowcs(NULL, str.c_str(), 0) + 1;
/* Allocate the output string (Add one to leave room for the NULL terminator) */
pWTempString = (wchar_t *)malloc( requiredSize * sizeof( wchar_t ));
if (pWTempString == NULL)
{
printf("Memory allocation failure.\n");
}
else
{
// Call the conversion function with the output buffer
size_t size = mbstowcs( pWTempString, str.c_str(), requiredSize);
if (size == (size_t) (-1))
{
printf("Couldn't convert string\n");
}
else
{
answer = pWTempString;
}
}
if (pWTempString != NULL)
{
delete[] pWTempString;
}
return answer;
}
int main()
{
std::string str = "abcd";
std::wstring wstr = wstring_from_bytes(str);
}
Regardless, this is much more easily done in newer versions of the standard library (C++ 11 and newer)
#include <locale>
#include <codecvt>
#include <string>
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring wide = converter.from_bytes(narrow_utf8_source_string);
printf-style format specifiers are not part of the C++ library and cannot be used to construct a string.
If the string may only contain single-byte characters, then the range constructor is sufficient.
std::string narrower( "hello" );
std::wstring wider( narrower.begin(), narrower.end() );
The problem is that we usually use wstring when wide characters are applicable (hence the w), which are represented in std::string by multibyte sequences. Doing this will cause each byte of a multibyte sequence to translate to an sequence of incorrect wide characters.
Moreover, to convert a multibyte sequence requires knowing its encoding. This information is not encapsulated by std::string nor std::wstring. C++11 allows you to specify an encoding and translate using std::wstring_convert, but I'm not sure how widely supported it is of yet. See 0x....'s excellent answer.
The converter mentioned for C++11 and above has deprecated this specific conversion in C++17, and suggests using the MultiByteToWideChar function.
The compiler error (c4996) mentions defining _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING.
wstring temp = L"";
for (auto c : DATA_DIR)
temp.push_back(c);
I found this function. Could not find any predefined method to do this.
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
std::wstring stemp = s2ws(myString);
I would like to convert a TCHAR array to a wstring.
TCHAR szFileName[MAX_PATH+1];
#ifdef _DEBUG
std::string str="m:\\compiled\\data.dat";
TCHAR *param=new TCHAR[str.size()+1];
szFileName[str.size()]=0;
std::copy(str.begin(),str.end(),szFileName);
#else
//Retrieve the path to the data.dat in the same dir as our data.dll is located
GetModuleFileName(_Module.m_hInst, szFileName, MAX_PATH+1);
StrCpy(PathFindFileName(szFileName), _T("data.dat"));
#endif
wstring sPath(T2W(szFileName));
I need to pass szFileName to a function that expects
const WCHAR *
For completeness I am stating the void that I need to pass szFileName to:
HRESULT CEngObj::MapFile( const WCHAR * pszTokenVal, // Value that contains file path
HANDLE * phMapping, // Pointer to file mapping handle
void ** ppvData ) // Pointer to the data
However, T2W does not work for me. The compiler says that "_lpa" is not defined, and I don't know where to go on from here. I have tried other conversion methods that I found stated on the net, but they did not work either.
There are functions like
mbstowcs_s()
that convert from char* to wchar_t*.
#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;
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;
Look here for an article and here for MSDN.
The definition of TCHAR differs depending on if certain preprocessor macros are defined or not. See e.g. this article for the possible combinations.
This means that TCHAR may already be a wchar_t.
You can use the _UNICODE macro to check if you need to convert the string. If you do, then you can use mbstowcs to do the conversion:
std::wstring str;
#ifdef _UNICODE
// No need to convert the string
str = your_tchar_string;
#else
// Need to convert the string
// First get the length needed
int length = mbstowcs(nullptr, your_tchar_string, 0);
// Allocate a temporary string
wchar_t* tmpstr = new wchar_t[length + 1];
// Do the actual conversion
mbstowcs(tmpstr, your_tchar_str, length + 1);
str = tmpstr;
// Free the temporary string
delete[] tmpstr;
#endif
I have gotten a WCHAR[MAX_PATH] from (PROCESSENTRY32) pe32.szExeFile on Windows. The following do not work:
std::string s;
s = pe32.szExeFile; // compile error. cast (const char*) doesnt work either
and
std::string s;
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,pe32.szExeFile,-1, ch,260,&DefChar, NULL);
s = pe32.szExeFile;
For your first example you can just do:
std::wstring s(pe32.szExeFile);
and for second:
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,pe32.szExeFile,-1, ch,260,&DefChar, NULL);
std::wstring s(pe32.szExeFile);
as std::wstring has a char* ctor
Your call to WideCharToMultiByte looks correct, provided ch is a
sufficiently large buffer. After than, however, you want to assign the
buffer (ch) to the string (or use it to construct a string), not
pe32.szExeFile.
There are convenient conversion classes from ATL; you may want to use some of them, e.g.:
std::string s( CW2A(pe32.szExeFile) );
Note however that a conversion from Unicode UTF-16 to ANSI can be lossy. If you wan't a non-lossy conversion, you could convert from UTF-16 to UTF-8, and store UTF-8 inside std::string.
If you don't want to use ATL, there are some convenient freely available C++ wrappers around raw Win32 WideCharToMultiByte to convert from UTF-16 to UTF-8 using STL strings.
#ifndef __STRINGCAST_H__
#define __STRINGCAST_H__
#include <vector>
#include <string>
#include <cstring>
#include <cwchar>
#include <cassert>
template<typename Td>
Td string_cast(const wchar_t* pSource, unsigned int codePage = CP_ACP);
#endif // __STRINGCAST_H__
template<>
std::string string_cast( const wchar_t* pSource, unsigned int codePage )
{
assert(pSource != 0);
size_t sourceLength = std::wcslen(pSource);
if(sourceLength > 0)
{
int length = ::WideCharToMultiByte(codePage, 0, pSource, sourceLength, NULL, 0, NULL, NULL);
if(length == 0)
return std::string();
std::vector<char> buffer( length );
::WideCharToMultiByte(codePage, 0, pSource, sourceLength, &buffer[0], length, NULL, NULL);
return std::string(buffer.begin(), buffer.end());
}
else
return std::string();
}
and use this template as followed
PWSTR CurWorkDir;
std::string CurWorkLogFile;
CurWorkDir = new WCHAR[length];
CurWorkLogFile = string_cast<std::string>(CurWorkDir);
....
delete [] CurWorkDir;