How to separate a executing path to file path and parameter? - c++

There are such lines as
C:\Program Files\Realtek\Audio\HDA\RAVCpl64.exe -s
in the registry key
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Now I want to separate the absolute path and parameters from the line.
If the line is
C:\Space Dir\Dot.Dir\Sample Name.Dot.exe param path."C:\Space Dir\Dot.Dir\Sample Name.Dot.exe"
Which separator should I use to deal with this line? Is there any Windows API function to solve this problem?

The function you want is in the standard C library that you can use in Windows.
char theDrive[5],thePath[MAX_PATH],theFilename[MAX_PATH],theExtension[MAX_PATH];
_splitpath(theSCTDataFilename,theDrive,thePath,theFilename,theExtension);
You can also use a more general tokenizing function like this which takes any string, a char and a CStringArray..
void tokenizeString(CString theString, TCHAR theToken, CStringArray *theParameters)
{
CString temp = "";
int i = 0;
for(i = 0; i < theString.GetLength(); i++ )
{
if (theString.GetAt(i) != theToken)
{
temp += theString.GetAt(i);
}
else
{
theParameters->Add(temp);
temp = "";
}
if(i == theString.GetLength()-1)
theParameters->Add(temp);
}
}
CStringArray thePathParts;
tokenizeString("your\complex\path\of\strings\separated\by\slashes",'/',&thePathParts);
This will give you an array of CString (CStringArray object) that contains each section of the input string. You can use this function to parse the major chunks then the minor ones as long as you know the seperator charactor you want to split the string on.

Related

c++ - Splitting an absolute file path

I'm writing a C++ program for a school assignment. At some point, the question requires me to change directories, which I know how to do. However, the user will provide the program with the absolute path of a file. What I'm trying to do is to change the directory to where that file is. For example, if I'm in a directory dir2, and the user want to go to the file
/home/dir1/dir2/dir3/dir4/file
I would like to do
int ret = chdir("home/dir1/dir2/dir3/dir4");
My question is how can I split the user-given string into
/home/dir1/dir2/dir3/dir4/
and
file
EDITI figured it out. I first converted the absolute pathname from a const char* to a string. Then I used the .find_last_of("/") string member to find the position of the last "/" in the string. Then I used the .substr() member to get the substring from 0 to that position returned by .find_last_of
Put your path into an std::string and then you can do something like the below.
std::string path = "/home/person/dir/file";
std::size_t botDirPos = path.find_last_of("/");
// get directory
std::string dir = path.substr(0, botDirPos);
// get file
std::string file = path.substr(botDirPos, path.length());
// change directory.
chdir(dir.c_str());
Every other answer to this question finds "/" (Unix) or "\" (Windows), and chops up the string manually; this is verbose and subject to user error. C++17 now has the std::filesystem package, which cleanly extracts directory and filename from a path in an OS friendly manner:
#include <filesystem>
void Test()
{
std::filesystem::path path("/home/dir1/dir2/dir3/dir4/file");
std::string dir = path.parent_path().string(); // "/home/dir1/dir2/dir3/dir4"
std::string file = path.filename().string(); // "file"
}
Simply get the last index of the "/" character in the file path, and snip the file with it's extension from the string.
1) Check that the directory listing has a "/". If not - throw an error.
2) Get the last index of the "/" in the string.
3) Return a sub string of the directory string, using the last index of function result (a number) as your starting index and the total length of the directory string.
Hope that helps.
you can use
std::string dir_str = "path/file";
auto pos = dir_str.rfind("/");
if (pos!= std::string::npos) {
chdir("newpath"+dir_str.substr(pos));
//...
} else {
//do something;
}
there may be issues such as character / in the file name. but assuming this is just a toy program designed for a simple test it should work.
if you are handling files somewhat seriously (like iterating through a directory recursively) I would recommend using something like boost::file_system.
You can use strtok function from <string.h> to split path components and by the way keep track of each dir in the hierarchy.
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="/path/to/file";
char * pch;
char * temp;
pch = strtok (str,"/");
while ( (temp = strtok (NULL, "/") ) != NULL)
{
pch = temp;
}
printf("The file is: %s", pch);
return 0;
}
To add to plethora of answers, I devised this after looking up stat struct and function:
struct ab_path{
int delimiter = 0;
int extension = 0;
int length = 0;
char separator = '\0';
ab_path(){}
operator bool()
{ return (this->delimiter != 0) | (this->extension != 0) | (this->length != 0) | (this->separator != '\0') ;}
};
bool ab_path( const char* name , struct ab_path* ap ){
while(1){
if(name[ap->length] == '\0'){break;}
if(name[ap->length] == '.') {ap->extension = ap->length;}
if(name[ap->length] == '/')
{ap->delimiter = ap->length; ap->separator = name[ap->length];}
if(name[ap->length] == '\\')
{ap->delimiter = ap->length;ap->separator = name[ap->length];}
++ap->length;
}
return (bool)ap;
}
struct ab_path ap;
bool valid = ap_path("superb.naming.type", &ap );
But you can rewrite an ap->delimiter to accept container of some sort (std::vector,std::array... ) and store multiple delimiters.
This might help.
What it does, it splits the file path with corresponding directories/files and store the names in a vector.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string filePath = "C:\\ProgramData\\Users\\CodeUncode\\Documents";
vector<string> directories;
size_t position=0, currentPosition=0;
while(currentPosition != -1)
{
currentPosition = filePath.find_first_of('\\', position);
directories.push_back(filePath.substr(position,currentPosition-position));
position = currentPosition+1;
}
for(vector<string>::iterator it = directories.begin(); it!=directories.end(); it++)
cout<<*it<<endl;
return 0;
}

Find Indexof in MFC C++

How can I find the indexof in GetWindowsText?
I just want to get extensions from box->fileExt->GetWindowsText(save);
For example my input is .exe .txt .bmp
So I want to get them separately. For example something like that:
.exe
.txt
.bmp
Currently my code is this:
for (int i = 0; i < files; i++)
{
box->testBox1.AddString(save);
fileExtensions.Add(save)`enter code here`;
CString check;
box->fileExt.GetWindowText(check);
CString store = check;
check.Find(' ') == save;
break;
continue;
if (fileExtensions[fileCounter] == store)
{
box->textBox2.AddString(fileExtensions[fileCounter]);
fileCounter++;
}
}//end for
It does not work.
You need to split string based on separator character(s). You can use Tokenize method of CString to do that:
CString sExtensions(_T(".exe .txt .bmp"));
CString sExt;
int nCurPos = 0;
CString sSeparators(_T(" ;"));
CStringArray Extensions;
sExt = sExtensions.Tokenize(sSeparators, nCurPos);
while (!sExt.IsEmpty())
{
Extensions.Add(sExt);
sExt = sExtensions.Tokenize(sSeparators, nCurPos);
}

Passing argument string which contains spaces and quotes

Using QProcess::startDetached, I need to pass a dynamic argument list which is coming from another process to the starting process.
const QString & prog, const QStringList & args, const QString & workingDirectory ...)
Note that arguments that contain spaces are not passed to the process
as separate arguments.
...
Windows: Arguments that contain spaces are wrapped in quotes. The
started process will run as a regular standalone process.
I have a string which contains below text, It comes from an external program without any control on it:
-c "resume" -c "print 'Hi!'" -c "print 'Hello World'"
I need to pass above string to QProcess::startDetached so that the starting program catches it as same as above string.
Do I have to parse the string and build a string-list? Or anyone has a better solution?
You don't have to use a QStringList at all for the arguments, as there is this overloaded function: -
bool QProcess::startDetached(const QString & program)
Which, as the documentation states: -
Starts the program program in a new process. program is a single string of text containing both the program name and its arguments. The arguments are separated by one or more spaces.
The program string can also contain quotes, to ensure that arguments containing spaces are correctly supplied to the new process.
You may need to replace " with \", but you can do that from a QString
You can use parseCombinedArgString (from Qt's source code) to parse:
QStringList parseCombinedArgString(const QString &program)
{
QStringList args;
QString tmp;
int quoteCount = 0;
bool inQuote = false;
// handle quoting. tokens can be surrounded by double quotes
// "hello world". three consecutive double quotes represent
// the quote character itself.
for (int i = 0; i < program.size(); ++i)
{
if (program.at(i) == QLatin1Char('"'))
{
++quoteCount;
if (quoteCount == 3)
{
// third consecutive quote
quoteCount = 0;
tmp += program.at(i);
}
continue;
}
if (quoteCount)
{
if (quoteCount == 1)
inQuote = !inQuote;
quoteCount = 0;
}
if (!inQuote && program.at(i).isSpace())
{
if (!tmp.isEmpty())
{
args += tmp;
tmp.clear();
}
}
else
{
tmp += program.at(i);
}
}
if (!tmp.isEmpty())
args += tmp;
return args;
}
Yes you have to "parse" the string, splitting it at the correct positions, and enter each sub-string into the QStringList object you pass to the function.

C++ TStringsList parse explanation

I'm trying to read a ini file in a value listbox.
Example below works, but i don't know why.
ReadSectionValues contains a string list of ini lines.
How does Rad Studio parse the lines with:
ListValues->Names[i] is first part of the line and ListValues->Values[ListValues->Names[i]] is the second part?
int i;
try
{
//ShowMessage( ListBox1->Items->Strings[ListBox1->ItemIndex] );
TStringList *ListValues = new TStringList;
TIniFile* SettingsFile = new TIniFile(ExtractFilePath(Application->ExeName) + "settings.ini");
String s;
s = ListBox1->Items->Strings[ListBox1->ItemIndex];
SettingsFile->ReadSectionValues( s , ListValues);
for (i = 0; i < (ListValues->Count); i++) {
//ShowMessage(ListValues->Names[i]);
//ShowMessage(ListValues->Values[ListValues->Names[i]]);
vList1->InsertRow(ListValues->Names[i] , ListValues->Values[ListValues->Names[i]],True);
}
delete SettingsFile;
delete ListValues;
}
catch(Exception* e)
{
ShowMessage(e->Message);
}
Please explain, Rad stuido help found no explanation.
void __fastcall ReadSectionValues(
const System::UnicodeString Section,
System::Classes::TStrings* Strings
)
is a method, which gets all lines of ini-file section with name Section and stores them in TStrings-object Strings. Note that these strings have format name=value.
TStrings class has two access properties Names and Values. Their parse algorithm is very simple. If you get stringsObject->Values[1] it takes second line from stringsObject and splits it into two strings on = (or other value of NameValueSeparator property of stringsObject). The string to the left of = (separator) is returned as name (by property Name) and the string to the right of = is returned as value (by property Value).

Arduino opening SD filename as string

I am trying to open up a file that I calculate the name into a string. However, it is just giving me compile errors as shown.
for(int i=1;;i++)
{
String temp = "data";
temp.concat(i);
temp.concat(".csv");
if(!SD.exists(temp))//no matching function for call to sdclass::exists(String&)
{
datur = SD.open(temp,FILE_WRITE);
}
}
I am a java person, so I don't see why this isn't working. I tried a few string object methods but none seem to have worked. I am a bit new at arduino programming but I understand java much better. The point of this for loop is to make a new file each time the arduino reboots.
SD.open expects a character array instead of a String, you need to convert it using the toCharArray method first. Try
char filename[temp.length()+1];
temp.toCharArray(filename, sizeof(filename));
if(!SD.exists(filename)) {
...
}
Completed Code:
for(int i=1;;i++)
{
String temp = "data";
temp.concat(i);
temp.concat(".csv");
char filename[temp.length()+1];
temp.toCharArray(filename, sizeof(filename));
if(!SD.exists(filename))
{
datur = SD.open(filename,FILE_WRITE);
break;
}
}
You will find a number of functions take char arrays instead of strings.