Change PATH environment variable for cmd.exe with QProcessEnvironment - c++

I want to start cmd.exe from a Qt app with a specific PATH set. I insert "Path" in QProcessEnvironment and set that environment to QProcess. Then I startDetached "cmd". On the command prompt, the path is the same as the from the calling app, not what I just set. Did I miss something? I use Qt 5.2.0 with mingw and Qt-creator 3.0.0 on windows 8.1.s
QProcess process(this);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("Path", "MyPath");
process.setProcessEnvironment(env);
QStringList args;
args << "/D" << "/K" << "dir";
process.startDetached("cmd", args);

The startDetached method is a static method. So all the state that you applied to the process object is just ignored, because the method cannot see it. If you start the process with start() instead, the new process will pick up your environment.
process.start("cmd", args);
Of course, you want to the new process to be detached so that the parent can terminate without forcing the new process also to terminate. From what I can tell, the QProcess class does not offer you a way to achieve that easily. You could modify the environment of the parent process so that the new process inherits those modifications, but that does not sound at all desirable.
This question presents a possible workaround: Detaching a started process.

As David answered startDetached doesn't use the environment. So I went and take the original code and adjusted it a bit so it works (for me at least).
WProcess.h:
#ifndef WPROCESS_H
#define WPROCESS_H
#include <QProcessEnvironment>
class WProcess
{
public:
WProcess();
void setProcessEnvironment(const QProcessEnvironment &value) {environment = value;}
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDir);
private:
QProcessEnvironment environment;
};
#endif // WPROCESS_H
WProcess.cpp:
#include "WProcess.h"
#include <Windows.h>
#include <WinBase.h>
static QString w_create_commandline(const QString &program, const QStringList &arguments)
{
QString args;
if (!program.isEmpty()) {
QString programName = program;
if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
// add the prgram as the first arg ... it works better
args = programName + QLatin1Char(' ');
}
for (int i=0; i<arguments.size(); ++i) {
QString tmp = arguments.at(i);
// Quotes are escaped and their preceding backslashes are doubled.
tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
// The argument must not end with a \ since this would be interpreted
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
int i = tmp.length();
while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
--i;
tmp.insert(i, QLatin1Char('"'));
tmp.prepend(QLatin1Char('"'));
}
args += QLatin1Char(' ') + tmp;
}
return args;
}
static QByteArray w_create_environment(const QProcessEnvironment &environment)
{
QByteArray envlist;
if (!environment.isEmpty())
{
static const wchar_t equal = L'=';
static const wchar_t nul = L'\0';
int pos = 0;
QStringList keys = environment.keys();
foreach(QString key, keys)
{
QString value = environment.value(key);
uint tmpSize = sizeof(wchar_t) * (key.length() + value.length() + 2);
// ignore empty strings
if (tmpSize != sizeof(wchar_t) * 2)
{
envlist.resize(envlist.size() + tmpSize);
tmpSize = key.length() * sizeof(wchar_t);
memcpy(envlist.data() + pos, key.utf16(), tmpSize);
pos += tmpSize;
memcpy(envlist.data() + pos, &equal, sizeof(wchar_t));
pos += sizeof(wchar_t);
tmpSize = value.length() * sizeof(wchar_t);
memcpy(envlist.data() + pos, value.utf16(), tmpSize);
pos += tmpSize;
memcpy(envlist.data() + pos, &nul, sizeof(wchar_t));
pos += sizeof(wchar_t);
}
}
// add the 2 terminating 0 (actually 4, just to be on the safe side)
envlist.resize( envlist.size()+4 );
envlist[pos++] = 0;
envlist[pos++] = 0;
envlist[pos++] = 0;
envlist[pos++] = 0;
}
return envlist;
}
WProcess::WProcess()
{
}
bool WProcess::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir)
{
QByteArray envlist;
if (!environment.isEmpty())
{
envlist = w_create_environment(environment);
}
QString args = w_create_commandline(program, arguments);
bool success = false;
PROCESS_INFORMATION pinfo;
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
success = CreateProcess(0, (wchar_t*)args.utf16(),
0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE,
envlist.isEmpty() ? 0 : envlist.data(),
workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
&startupInfo, &pinfo);
if (success)
{
CloseHandle(pinfo.hThread);
CloseHandle(pinfo.hProcess);
//if (pid) *pid = pinfo.dwProcessId;
}
return success;
}
Usage, open command prompt in C:\Qt\Qt-creator and set path to "mypath".
WProcess process;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("Path", "mypath");
process.setProcessEnvironment(env);
process.startDetached("cmd", QStringList(), "C:\\Qt\\Qt-creator");

Related

Iterate directory or files with wildcards in c++ [duplicate]

how can I easy get all files paths from path containing a wildcards? For example: C:/Data*Set/Files*/*.txt and I wrote it on Linux using glob function but I can't do it on Windows :/
FindFirstFile unfortunately doesn't support wildcards in directory names.
I think there should be a Windows solution available but I can't find it.
So you should do away with using OS specific file access, in favor of the OS independent: Filesystem Library
Let's say that you're given filesystem::path input which contains the path with wildcards. To use this to solve your problem you'd need to:
Use parent_path to break apart input into directories
Use filename to obtain the input filename
Obtain a directory_iterator to the relative or absolute path where the input begins
Create a recursive function which takes in begin and end iterators to the obtained parent path, the directory iterator, and the filename
Any time a directory or filename uses a '*' use a regex with the iterator to determine the directory which should be progressed to next
Either return the path to the matching file or an empty path
Due to the excellent Ben Voigt's comment I've updated the algorithm to step over unwildcarded directories.
For example:
regex GenerateRegex(string& arg) {
for (auto i = arg.find('*'); i != string::npos; i = arg.find('*', i + 2)) {
arg.insert(i, 1, '.');
}
return regex(arg);
}
filesystem::path FindFirstFile(filesystem::path directory, filesystem::path::const_iterator& start, const filesystem::path::const_iterator& finish, string& filename) {
while (start != finish && start->string().find('*') == string::npos) {
directory /= *start++;
}
filesystem::directory_iterator it(directory);
filesystem::path result;
if (it != filesystem::directory_iterator()) {
if (start == finish) {
for (auto i = filename.find('.'); i != string::npos; i = filename.find('.', i + 2)) {
filename.insert(i, 1, '\\');
}
const auto re = GenerateRegex(filename);
do {
if (!filesystem::is_directory(it->status()) && regex_match(it->path().string(), re)) {
result = *it;
break;
}
} while (++it != filesystem::directory_iterator());
}
else {
const auto re = GenerateRegex(start->string());
do {
if (it->is_directory() && regex_match(prev(it->path().end())->string(), re)) {
result = FindFirstFile(it->path(), next(start), finish, filename);
if (!result.empty()) {
break;
}
}
} while (++it != filesystem::directory_iterator());
}
}
return result;
}
Which can be called with:
const filesystem::path input("C:/Test/Data*Set/Files*/*.txt");
if (input.is_absolute()) {
const auto relative_parent = input.parent_path().relative_path();
cout << FindFirstFile(input.root_path(), begin(relative_parent), end(relative_parent), input.filename().string()) << endl;
} else {
const auto parent = input.parent_path();
cout << FindFirstFile(filesystem::current_path(), begin(parent), end(parent), input.filename().string()) << endl;
}
Live Example
need understand how FindFirstFile[Ex] work. this is shell over NtQueryDirectoryFile. FindFirstFile[Ex] need divide input name to folder name (which will be opened in used as FileHandle) and search mask used as FileName. mask can be only in file name. folder must have exact name without wildcard to opened first.
as result FindFirstFile[Ex] always open concrete single folder and search in this folder by mask. for recursive search files - we need recursive call FindFirstFile[Ex]. simply usual we use the same constant search mask on all levels. for example when we want find all files begin from X:\SomeFolder we first call FindFirstFile[Ex] with X:\SomeFolder\* on level 0. if we found SomeSubfolder - we call FindFirstFile[Ex] with X:\SomeFolder\SomeSubfolder\* on level 1 and so on. but we can use different search masks on different levels. Data*Set on level 0, Files* on level 1, *.txt on level 2
so we need call FindFirstFileEx recursive and on different recursions level use different masks. for example we want found c:\Program*\*\*.txt. we need start from c:\Program*, then for every founded result append \* mask, then append \*.txt on next level. or we can for example want next - search files by next mask - c:\Program Files*\Internet Explorer\* with any deep level. we can use constant deep search folder mask (optional) with final mask (also optional) used already on all more deep levels.
all this can be really not so hard and efficient implemented:
struct ENUM_CONTEXT : WIN32_FIND_DATA
{
PCWSTR _szMask;
PCWSTR *_pszMask;
ULONG _MaskCount;
ULONG _MaxLevel;
ULONG _nFiles;
ULONG _nFolders;
WCHAR _FileName[MAXSHORT + 1];
void StartEnum(PCWSTR pcszRoot, PCWSTR pszMask[], ULONG MaskCount, PCWSTR szMask, ULONG MaxLevel, PSTR prefix)
{
SIZE_T len = wcslen(pcszRoot);
if (len < RTL_NUMBER_OF(_FileName))
{
memcpy(_FileName, pcszRoot, len * sizeof(WCHAR));
_szMask = szMask, _pszMask = pszMask, _MaskCount = MaskCount;
_MaxLevel = szMask ? MaxLevel : MaskCount;
_nFolders = 0, _nFolders = 0;
Enum(_FileName + len, 0, prefix);
}
}
void Enum(PWSTR pszEnd, ULONG nLevel, PSTR prefix);
};
void ENUM_CONTEXT::Enum(PWSTR pszEnd, ULONG nLevel, PSTR prefix)
{
if (nLevel > _MaxLevel)
{
return ;
}
PCWSTR lpFileName = _FileName;
SIZE_T cb = lpFileName + RTL_NUMBER_OF(_FileName) - pszEnd;
PCWSTR szMask = nLevel < _MaskCount ? _pszMask[nLevel] : _szMask;
SIZE_T cchMask = wcslen(szMask) + 1;
if (cb < cchMask + 1)
{
return ;
}
*pszEnd++ = L'\\', cb--;
DbgPrint("%s[<%.*S>]\n", prefix, pszEnd - lpFileName, lpFileName);
memcpy(pszEnd, szMask, cchMask * sizeof(WCHAR));
ULONG dwError;
HANDLE hFindFile = FindFirstFileEx(lpFileName, FindExInfoBasic, this, FindExSearchNameMatch, 0, FIND_FIRST_EX_LARGE_FETCH);
if (hFindFile != INVALID_HANDLE_VALUE)
{
PWSTR FileName = cFileName;
do
{
SIZE_T FileNameLength = wcslen(FileName);
switch (FileNameLength)
{
case 2:
if (FileName[1] != '.') break;
case 1:
if (FileName[0] == '.') continue;
}
if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
_nFolders++;
if (!(dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
{
if (cb < FileNameLength)
{
__debugbreak();
}
else
{
memcpy(pszEnd, FileName, FileNameLength * sizeof(WCHAR));
Enum(pszEnd + FileNameLength, nLevel + 1, prefix - 1);
}
}
}
else if (nLevel >= _MaskCount || (!_szMask && nLevel == _MaskCount - 1))
{
_nFiles++;
DbgPrint("%s%u%u <%.*S>\n", prefix, nFileSizeLow, nFileSizeHigh, FileNameLength, FileName);
}
} while (FindNextFile(hFindFile, this));
if ((dwError = GetLastError()) == ERROR_NO_MORE_FILES)
{
dwError = NOERROR;
}
FindClose(hFindFile);
}
else
{
dwError = GetLastError();
}
if (dwError && dwError != ERROR_FILE_NOT_FOUND)
{
DbgPrint("%s[<%.*S>] err = %u\n", prefix, pszEnd - lpFileName, lpFileName, dwError);
}
}
void Test(PCWSTR pcszRoot)
{
char prefix[MAXUCHAR + 1];
memset(prefix, '\t', RTL_NUMBER_OF(prefix) - 1);
prefix[RTL_NUMBER_OF(prefix) - 1] = 0;
ENUM_CONTEXT ectx;
static PCWSTR Masks[] = { L"Program*", L"*", L"*.txt" };
static PCWSTR Masks2[] = { L"Program*", L"*" };
static PCWSTR Masks3[] = { L"Program Files*", L"Internet Explorer" };
// search Program*\*\*.txt with fixed deep level
ectx.StartEnum(pcszRoot, Masks, RTL_NUMBER_OF(Masks), 0, RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
// search *.txt files from Program*\*\ - any deep level
ectx.StartEnum(pcszRoot, Masks2, RTL_NUMBER_OF(Masks2), L"*.txt", RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
// search all files (*) from Program Files*\Internet Explorer\
ectx.StartEnum(pcszRoot, Masks3, RTL_NUMBER_OF(Masks3), L"*", RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
}

How do I enumerate volumes on MacOSX in C++?

I'm brand new to Mac development/xcode. I'm trying to do what I feel should be extremely simple, but over a week of research has yielded no results.
I want to list the external usb drive available as a vector of strings.
I don't want their cryptic information like address serial or anything. I just want their paths IE: "D:/" or "Sandisk USB".
I accomplished this in Windows quite easily using the code found below, but finding out how to do this on Mac has me pulling my hair out.
The only thing I've found seems to be done for Objective C, - How to enumerate volumes on Mac OS X?
but my project uses c++.
Can someone please provide a simple example or point me in the right direction.
struct ESDriveDescription
{
std::string path;
std::string label;
ESDriveDescription() = default;
ESDriveDescription(const std::string &path, const std::string &label)
: path(path), label(label)
{}
};
int ESFileUtils::getExternalStorageDevicePaths(vector<ESDriveDescription> &paths){
// Letters in alphabet * 3 characters per drive path, + nul term + final nul
// NOTE: constexpr not supported in vs2013
static const DWORD DRIVE_BUFFER_SIZE = 26 * 4 + 1;
static const DWORD VOLUME_LABEL_MAX = 32;
const char* removableDriveNames[26] = { 0 };
char allDrives[DRIVE_BUFFER_SIZE] = { 0 };
int numRemovableDrives = 0;
DWORD n = GetLogicalDriveStringsA(DRIVE_BUFFER_SIZE, allDrives);
for (DWORD i = 0; i < n; i += 4) {
const char* driveName = &allDrives[i];
UINT type = GetDriveTypeA(driveName);
if (type == DRIVE_REMOVABLE)
removableDriveNames[numRemovableDrives++] = driveName;
}
char label[VOLUME_LABEL_MAX] = { 0 };
for (int i = 0; i < numRemovableDrives; i++) {
const char* driveName = removableDriveNames[i];
GetVolumeInformationA(driveName, label, VOLUME_LABEL_MAX, 0, 0, 0, 0, 0);
paths.emplace_back(driveName, label);
}
return numRemovableDrives;
}

syntax highlight richedit control not working poperly

I am trying to implement a syntax highlight editor with a richedit, it has works well with the current selected line, but I may be missing something. The CRichEdit is my own wrapper implementation of the richedit controller, the problem seems that the text is not selected properly even though I made sure the selected range generated with the code were what I get with the EM_EXGETSEL message.
The selection seems increasing 1 as the lines goes down, so I decided to ed_source.sendMessage(EM_LINEFROMCHAR, pos, 0) to the range which partially fix the problem except for some few lines where the coloring seems some time one or to position before and the real appropriate, so that is why I thing I may not be understanding something.
void parse(WIN::CRichEdit &ed_source, bool curseline)
{
int pos, offset = 0;
char delimiter[]={" \n\r(){};"}, *tok, *start;
CStringA s;
CString text;
CWnd api;
if(curseline){
ed_source.getLine(ed_source.getRow() - 1, text);
offset = ed_source.sendMessage(EM_LINEINDEX, -1, 0);
}else{
text = ed_source.getCaption();
}
s = text;
start = s.c_str();
if(!start) return;
tok = strtok(s.c_str(), delimiter);
CHARRANGE cr = ed_source.getSelecteRange();
ed_source.sendMessage(EM_HIDESELECTION, 1, 0) ;
CHARRANGE range;
while(tok)
{
int len = strlen(tok);
pos = (tok - start);
int x = ed_source.sendMessage(EM_LINEFROMCHAR, pos, 0);
range.cpMin = offset + pos - x;
range.cpMax = range.cpMin + len;
ed_source.selectRange(range);
if(isReserved(tok)){
ed_source.setTextStyle(true, false);
ed_source.setTextColor(keyboardColor);
}else
if(isType(tok)){
ed_source.setTextStyle(false, false);
ed_source.setTextColor(typeColor);
}else {
ed_source.setTextStyle(false, true);
ed_source.setTextColor(textColor);
}
tok = strtok(0, delimiter);
}
ed_source.sendMessage(EM_HIDESELECTION, 0, 0) ;
ed_source.selectRange(cr);
}
just to be more specific the moment I call the above function is immediately after loading the text on it. I assumed you may want to see the implementation of some of the above functions so here they are.
CHARRANGE CRichEdit::getSelecteRange()
{
CHARRANGE crg = {0} ;
sendMessage(EM_EXGETSEL, 0, (LPARAM)&crg);
return crg;
}
void CRichEdit::selectRange(const CHARRANGE &cr)
{
sendMessage( EM_EXSETSEL, 0, (LPARAM) &cr);
}
void CRichEdit::setTextColor(COLORREF col)
{
CHARFORMAT format;
memset(&format, 0, sizeof(CHARFORMAT));
format.cbSize = sizeof(CHARFORMAT);
format.dwMask = CFM_COLOR;
format.crTextColor = col;
sendMessage( EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &format);
}
Have a look at this article for some ideas:
Faster rich edit syntax highlighting
http://bcbjournal.org/articles/vol3/9910/Faster_rich_edit_syntax_highlighting.htm
It was written for the TRichEdit control in C++Builder, but the majority of the tips in it use straight Win32 API calls, and the few places that use VCL idioms can be easily adapted into Win32 equivilents.
Update: Try removing EM_LINEFROMCHAR from your loop. offset + pos is already an absolute character position within the RichEdit, there should be no need to adjust it on each loop iteration. If you really want to take line indexes into account then you should be looping through the lines one row at a time parsing each row individually, not parsing the entire content as a single string. Try something more like this instead:
void parse(WIN::CRichEdit &ed_source, bool curseline)
{
int startLine, endLine, offset;
const char* delimiters = " \n\r(){};";
char *tok, *start;
CStringA s;
CWnd api;
if (curseline)
{
startLine = ed_source.getRow() - 1;
endLine = startLine + 1;
}
else
{
startLine = 0;
endLine = ed_source.sendMessage(EM_GETLINECOUNT, 0, 0);
}
CHARRANGE cr = ed_source.getSelecteRange();
int eventMask = ed_source.SendMessage(EM_SETEVENTMASK, 0, 0);
ed_source.SendMessage(WM_SETREDRAW, FALSE, 0);
for (int line = startLine; line < endLine; ++line)
{
CString text;
ed_source.getLine(line, text);
s = text;
start = s.c_str();
if (!start) continue;
offset = ed_source.sendMessage(EM_LINEINDEX, line, 0);
tok = strtok(start, delimiters);
while (tok)
{
CHARRANGE range;
range.cpMin = offset + (int)(tok - start);
range.cpMax = range.cpMin + strlen(tok);
ed_source.selectRange(range);
if (isReserved(tok))
{
ed_source.setTextStyle(true, false);
ed_source.setTextColor(keyboardColor);
}
else if (isType(tok))
{
ed_source.setTextStyle(false, false);
ed_source.setTextColor(typeColor);
}
else
{
ed_source.setTextStyle(false, true);
ed_source.setTextColor(textColor);
}
tok = strtok(0, delimiters);
}
}
ed_source.SendMessage(WM_SETREDRAW, TRUE, 0);
ed_source.Invalidate(); // whatever your wrapper does to call ::InvalidateRect()
ed_source.SendMessage(EM_SETEVENTMASK, 0, eventMask);
ed_source.selectRange(cr);
}
With that said, instead of using getLine() and strtok() to parse text, you might consider using EM_FINDWORDBREAK to locate words and EM_EXSETSEL/EM_GETSELTEXT to retreive the characters of each word. That way, you are using less memory and letting the RichEdit do more of the searching for you. You can use EM_SETWORDBREAKPROC/EX if you want to customize the word delimiters searched for.

QString parse system variables

How I should parse QString, which contains system variables ?What I want:
QString path = "%WINDIR%\\System32\\";
QString output = parse(path);
QDebug()<<output; \\ output is "C:\\Windows\\System32\\"
I think you want something like this:
// Untested
QString parse(QString str)
{
int pos = 0;
QRegExp rx("%([^%]+)%"); // Match env var between two '%'
rx.setMinimal(true);
while((pos = rx.indexIn(str, pos)) != -1)
{
// Replace env var
QString capture = rx.cap(1);
QString replacement = getenv(capture.toAscii());
str.replace("%" + capture + "%", replacement);
// Skip env var + two '%'
pos += rx.matchedLength() + 2;
}
return str;
}
QString path = parse("%WINDIR%\\System32");
I think, this is what you looking for. Please try this
QString windir = getenv ("WINDIR"); // Expanded
if (windir.isEmpty()) {
fprintf(stderr, "Generator requires WINDIRto be set\n");
}
windir += "\\System32";
qDebug()<<windir;

How can i convert entity character(Escape character) to HTML in QT?

I want to convert entity character(Escape character) to HTML in QT, please help me....
i.e: I want to replace " with ", > with >
=====This is my code that not worked====
QString MyApp::ReplaceString(const QString Data, const QString &Before, const QString &After)
{
QString Result = Data;
Result.replace(Before, After, Qt::CaseInsensitive);
return Result;
}
========
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QByteArray data=pReply->readAll();
QString str = codec->toUnicode((const char *)data);
str = Qt::escape(str);
str = ReplaceString(str, """, "\"");
str = ReplaceString(str,">", ">");
I'm not sure I understand what you want, just guessing. You can use QTextDocument. Try something like this:
QTextDocument text;
text.setHtml("<>"");
QString plain = text.toPlainText();
qDebug("%s.", qPrintable(plain));
Remember that QTextDocument needs the gui module.
I think this will solve your problem.
QString escaped=
QString(myhtml).replace("&","&").replace(">",">").replace("<","<");
Test escape() function:
QString plain = "#include <QtCore>"
QString html = Qt::escape(plain);
// html == "#include <QtCore>"
and convertFromPlainText() function:
QString Qt::convertFromPlainText ( const QString & plain, WhiteSpaceMode mode = WhiteSpacePre )
Hello to convert non ASCII character to &#XXX; (where XXX is a number):
/***************************************************************************//*!
* #brief Encode all non ASCII characters into &#...;
* #param[in] src Text to analyze
* #param[in,opt] force Force the characters "list" to be converted.
* #return ASCII text compatible.
*
* #note Original code: http://www.qtforum.org/article/3891/text-encoding.html
*
* #warning Do not forget to use QString::fromUtf8()
*/
QString encodeEntities( const QString& src, const QString& force=QString() )
{
QString tmp(src);
uint len = tmp.length();
uint i = 0;
while( i<len )
{
if( tmp[i].unicode() > 128 || force.contains(tmp[i]) ){
QString rp = "&#"+QString::number(tmp[i].unicode())+";";
tmp.replace(i,1,rp);
len += rp.length()-1;
i += rp.length();
}else{
++i;
}
}
return tmp;
}
/***************************************************************************//*!
* #brief Allows decode &#...; into UNICODE (utf8) character.
* #param[in] src Text to analyze
* #return UNICODE (utf8) text.
*
* #note Do not forget to include QRegExp
*/
QString decodeEntities( const QString& src )
{
QString ret(src);
QRegExp re("&#([0-9]+);");
re.setMinimal(true);
int pos = 0;
while( (pos = re.indexIn(src, pos)) != -1 )
{
ret = ret.replace(re.cap(0), QChar(re.cap(1).toInt(0,10)));
pos += re.matchedLength();
}
return ret;
}
Basic usage:
qDebug() << encodeEntities(QString::fromUtf8("éà#<>hello the world €"),QString("<>"));
// Will print: éà#<>hello the world €
qDebug() << decodeEntities("aßéplopéàçê€");
// Will print: hello world